From 7dbedb9214c85f52c62c23db2bd4bee3a6082084 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 3 Mar 2022 15:40:48 -0600 Subject: [PATCH 001/140] starting to gather necessary data in embeddable --- .../public/app_plugin/show_underlying_data.ts | 5 +- .../public/editor_frame_service/service.tsx | 2 +- .../lens/public/embeddable/embeddable.tsx | 56 +++++++++++++++++-- .../public/embeddable/embeddable_factory.ts | 7 ++- x-pack/plugins/lens/public/plugin.ts | 32 ++++++++++- 5 files changed, 93 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts index a8c678167f70f..f56edff12cf86 100644 --- a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts +++ b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts @@ -53,7 +53,10 @@ export function getLayerMetaInfo( currentDatasource: Datasource | undefined, datasourceState: unknown, activeData: TableInspectorAdapter | undefined, - capabilities: RecursiveReadonly + capabilities: RecursiveReadonly<{ + navLinks: Capabilities['navLinks']; + discover?: Capabilities['discover']; + }> ): { meta: LayerMetaInfo | undefined; isVisible: boolean; error: string | undefined } { const isVisible = Boolean(capabilities.navLinks?.discover && capabilities.discover?.show); // If Multiple tables, return diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index b585d03e12f8f..ff05f7e5f0bb9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -61,7 +61,7 @@ export class EditorFrameService { private readonly datasources: Array Promise)> = []; private readonly visualizations: Array Promise)> = []; - private loadDatasources = () => collectAsyncDefinitions(this.datasources); + public loadDatasources = () => collectAsyncDefinitions(this.datasources); public loadVisualizations = () => collectAsyncDefinitions(this.visualizations); /** diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index aa0a9de248c1b..8d336999ffe99 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -56,21 +56,24 @@ import { LensTableRowContextMenuEvent, VisualizationMap, Visualization, + DatasourceMap, } from '../types'; import { IndexPatternsContract } from '../../../../../src/plugins/data/public'; import { getEditPath, DOC_TYPE, PLUGIN_ID } from '../../common'; import type { + Capabilities, IBasePath, KibanaExecutionContext, ThemeServiceStart, } from '../../../../../src/core/public'; import { LensAttributeService } from '../lens_attribute_service'; -import type { ErrorMessage } from '../editor_frame_service/types'; +import type { ErrorMessage, TableInspectorAdapter } from '../editor_frame_service/types'; import { getLensInspectorService, LensInspector } from '../lens_inspector_service'; import { SharingSavedObjectProps } from '../types'; import type { SpacesPluginStart } from '../../../spaces/public'; -import { inferTimeField } from '../utils'; +import { getActiveDatasourceIdFromDoc, getIndexPatternsObjects, inferTimeField } from '../utils'; +import { getLayerMetaInfo, combineQueryAndFilters } from '../app_plugin/show_underlying_data'; export type LensSavedObjectAttributes = Omit; @@ -115,6 +118,7 @@ export interface LensEmbeddableDeps { ) => Promise<{ ast: Ast | null; errors: ErrorMessage[] | undefined }>; injectFilterReferences: FilterManager['inject']; visualizationMap: VisualizationMap; + datasourceMap: DatasourceMap; indexPatternService: IndexPatternsContract; expressionRenderer: ReactExpressionRendererType; timefilter: TimefilterContract; @@ -122,7 +126,12 @@ export interface LensEmbeddableDeps { inspector: InspectorStart; getTrigger?: UiActionsStart['getTrigger'] | undefined; getTriggerCompatibleActions?: UiActionsStart['getTriggerCompatibleActions']; - capabilities: { canSaveVisualizations: boolean; canSaveDashboards: boolean }; + capabilities: { + canSaveVisualizations: boolean; + canSaveDashboards: boolean; + navLinks: Capabilities['navLinks']; + discover?: Capabilities['discover']; + }; usageCollection?: UsageCollectionSetup; spaces?: SpacesPluginStart; theme: ThemeServiceStart; @@ -388,7 +397,10 @@ export class Embeddable return isDirty; } - private updateActiveData: ExpressionWrapperProps['onData$'] = () => { + private updateActiveData: ExpressionWrapperProps['onData$'] = (data) => { + this.getViewUnderlyingDataArgs(data?.value?.data?.tables as TableInspectorAdapter).then( + console.log + ); if (this.input.onLoad) { // once onData$ is get's called from expression renderer, loading becomes false this.input.onLoad(false); @@ -598,6 +610,42 @@ export class Embeddable } } + async getViewUnderlyingDataArgs(activeData: TableInspectorAdapter) { + const activeDatasourceId = getActiveDatasourceIdFromDoc(this.savedVis); + + if (!activeDatasourceId) { + return; + } + + const { error, meta } = getLayerMetaInfo( + this.deps.datasourceMap[activeDatasourceId], + (this.savedVis?.state.datasourceStates[activeDatasourceId] as { state: unknown }).state, + activeData, + this.deps.capabilities + ); + + if (error || !meta) { + return; + } + + const { indexPatterns } = await getIndexPatternsObjects([], this.deps.indexPatternService); + + const { filters: newFilters, query: newQuery } = combineQueryAndFilters( + this.input.query, + this.input.filters || [], + meta, + indexPatterns + ); + + return { + indexPatternId: meta.id, + timeRange: this.input.timeRange, + filters: newFilters, + query: newQuery, + columns: meta.columns, + }; + } + async initializeOutput() { if (!this.savedVis) { return; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts index fc335bf2f5f87..7e560b5a2882c 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/embeddable/embeddable_factory.ts @@ -29,7 +29,7 @@ import { DOC_TYPE } from '../../common/constants'; import { ErrorMessage } from '../editor_frame_service/types'; import { extract, inject } from '../../common/embeddable_factory'; import type { SpacesPluginStart } from '../../../spaces/public'; -import { VisualizationMap } from '../types'; +import { DatasourceMap, VisualizationMap } from '../types'; export interface LensEmbeddableStartServices { timefilter: TimefilterContract; @@ -46,6 +46,7 @@ export interface LensEmbeddableStartServices { ) => Promise<{ ast: Ast | null; errors: ErrorMessage[] | undefined }>; injectFilterReferences: FilterManager['inject']; visualizationMap: VisualizationMap; + datasourceMap: DatasourceMap; spaces?: SpacesPluginStart; theme: ThemeServiceStart; } @@ -95,6 +96,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { documentToExpression, injectFilterReferences, visualizationMap, + datasourceMap, uiActions, coreHttp, attributeService, @@ -121,9 +123,12 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { documentToExpression, injectFilterReferences, visualizationMap, + datasourceMap, capabilities: { canSaveDashboards: Boolean(capabilities.dashboard?.showWriteControls), canSaveVisualizations: Boolean(capabilities.visualize.save), + navLinks: capabilities.navLinks, + discover: capabilities.discover, }, usageCollection, theme, diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index cfd0f106fae1c..9dfd8be1d5c46 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -16,7 +16,12 @@ import type { DataPublicPluginSetup, DataPublicPluginStart, } from '../../../../src/plugins/data/public'; -import type { EmbeddableSetup, EmbeddableStart } from '../../../../src/plugins/embeddable/public'; +import { + CONTEXT_MENU_TRIGGER, + EmbeddableSetup, + EmbeddableStart, + IEmbeddable, +} from '../../../../src/plugins/embeddable/public'; import type { DashboardStart } from '../../../../src/plugins/dashboard/public'; import type { SpacesPluginStart } from '../../spaces/public'; import type { @@ -67,9 +72,15 @@ import { UiActionsStart, ACTION_VISUALIZE_FIELD, VISUALIZE_FIELD_TRIGGER, + createAction, } from '../../../../src/plugins/ui_actions/public'; import { VISUALIZE_EDITOR_TRIGGER } from '../../../../src/plugins/visualizations/public'; -import { APP_ID, getEditPath, NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common/constants'; +import { + APP_ID, + DOC_TYPE, + getEditPath, + NOT_INTERNATIONALIZED_PRODUCT_NAME, +} from '../common/constants'; import type { FormatFactory } from '../common/types'; import type { Visualization, @@ -252,6 +263,7 @@ export class LensPlugin { plugins.fieldFormats.deserialize ); const visualizationMap = await this.editorFrameService!.loadVisualizations(); + const datasourceMap = await this.editorFrameService!.loadDatasources(); return { attributeService: getLensAttributeService(coreStart, plugins), @@ -262,6 +274,7 @@ export class LensPlugin { documentToExpression: this.editorFrameService!.documentToExpression, injectFilterReferences: data.query.filterManager.inject.bind(data.query.filterManager), visualizationMap, + datasourceMap, indexPatternService: plugins.data.indexPatterns, uiActions: plugins.uiActions, usageCollection, @@ -430,6 +443,21 @@ export class LensPlugin { visualizeTSVBAction(core.application) ); + startDependencies.uiActions.addTriggerAction( + CONTEXT_MENU_TRIGGER, + createAction<{ embeddable: IEmbeddable }>({ + type: 'VIEW_UNDERLYING_DATA', + id: 'VIEW_UNDERLYING_DATA', + getDisplayName: () => 'Open in Discover', + isCompatible: async (context: { embeddable: IEmbeddable }) => { + return context.embeddable.type === DOC_TYPE; + }, + execute: async (context: { embeddable: IEmbeddable }) => { + alert('FOOBAR'); + }, + }) + ); + return { EmbeddableComponent: getEmbeddableComponent(core, startDependencies), SaveModalComponent: getSaveModalComponent(core, startDependencies), From 10b88d580dd8633fc657a7ccd872ad809cbd0f49 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 3 Mar 2022 17:29:39 -0600 Subject: [PATCH 002/140] Computing view underlying data args --- .../lens/public/embeddable/embeddable.tsx | 145 ++++++++++++------ 1 file changed, 94 insertions(+), 51 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 8d336999ffe99..3344a033fb2ff 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -57,6 +57,7 @@ import { VisualizationMap, Visualization, DatasourceMap, + Datasource, } from '../types'; import { IndexPatternsContract } from '../../../../../src/plugins/data/public'; @@ -148,6 +149,52 @@ const getExpressionFromDocument = async ( }; }; +async function getViewUnderlyingDataArgs({ + activeDatasource, + activeDatasourceState, + activeData, + indexPatterns, + capabilities, + query, + filters, + timeRange, +}: { + activeDatasource: Datasource; + activeDatasourceState: unknown; + activeData: TableInspectorAdapter | undefined; + indexPatterns: IndexPattern[]; + capabilities: LensEmbeddableDeps['capabilities']; + query: Query; + filters: Filter[]; + timeRange: TimeRange; +}) { + const { error, meta } = getLayerMetaInfo( + activeDatasource, + activeDatasourceState, + activeData, + capabilities + ); + + if (error || !meta) { + return; + } + + const { filters: newFilters, query: newQuery } = combineQueryAndFilters( + query, + filters, + meta, + indexPatterns + ); + + return { + indexPatternId: meta.id, + timeRange, + filters: newFilters, + query: newQuery, + columns: meta.columns, + }; +} + export class Embeddable extends AbstractEmbeddable implements ReferenceOrValueEmbeddable @@ -183,6 +230,14 @@ export class Embeddable searchSessionId?: string; } = {}; + private activeDataInfo: { + activeData?: TableInspectorAdapter; + activeDatasource?: Datasource; + activeDatasourceState?: unknown; + } = {}; + + private indexPatterns: IndexPattern[] = []; + constructor( private deps: LensEmbeddableDeps, initialInput: LensEmbeddableInput, @@ -197,7 +252,12 @@ export class Embeddable ); this.lensInspector = getLensInspectorService(deps.inspector); this.expressionRenderer = deps.expressionRenderer; - this.initializeSavedVis(initialInput).then(() => this.onContainerStateChanged(initialInput)); + this.initializeSavedVis(initialInput) + .then(() => this.initializeOutput()) + .then(() => { + this.isInitialized = true; + this.onContainerStateChanged(initialInput); + }); this.subscription = this.getUpdated$().subscribe(() => this.onContainerStateChanged(this.input) ); @@ -364,9 +424,6 @@ export class Embeddable ); this.expression = expression; this.errors = this.maybeAddConflictError(errors, metaInfo?.sharingSavedObjectProps); - - await this.initializeOutput(); - this.isInitialized = true; } onContainerStateChanged(containerState: LensEmbeddableInput) { @@ -392,15 +449,15 @@ export class Embeddable searchSessionId: containerState.searchSessionId, }; this.embeddableTitle = this.getTitle(); + this.updateViewUnderlyingDataArgs(); isDirty = true; } return isDirty; } - private updateActiveData: ExpressionWrapperProps['onData$'] = (data) => { - this.getViewUnderlyingDataArgs(data?.value?.data?.tables as TableInspectorAdapter).then( - console.log - ); + private updateActiveData: ExpressionWrapperProps['onData$'] = (_, adapters) => { + this.activeDataInfo.activeData = adapters?.tables?.tables; + this.updateViewUnderlyingDataArgs(); if (this.input.onLoad) { // once onData$ is get's called from expression renderer, loading becomes false this.input.onLoad(false); @@ -610,65 +667,51 @@ export class Embeddable } } - async getViewUnderlyingDataArgs(activeData: TableInspectorAdapter) { + private async updateViewUnderlyingDataArgs() { const activeDatasourceId = getActiveDatasourceIdFromDoc(this.savedVis); - - if (!activeDatasourceId) { - return; - } - - const { error, meta } = getLayerMetaInfo( - this.deps.datasourceMap[activeDatasourceId], - (this.savedVis?.state.datasourceStates[activeDatasourceId] as { state: unknown }).state, - activeData, - this.deps.capabilities - ); - - if (error || !meta) { - return; + if (activeDatasourceId) { + this.activeDataInfo.activeDatasource = this.deps.datasourceMap[activeDatasourceId]; + const docDatasourceState = this.savedVis?.state.datasourceStates[activeDatasourceId]; + + this.activeDataInfo.activeDatasourceState = + await this.activeDataInfo.activeDatasource.initialize( + docDatasourceState, + this.savedVis?.references + ); } - const { indexPatterns } = await getIndexPatternsObjects([], this.deps.indexPatternService); - - const { filters: newFilters, query: newQuery } = combineQueryAndFilters( - this.input.query, - this.input.filters || [], - meta, - indexPatterns - ); + const viewUnderlyingDataArgs = await getViewUnderlyingDataArgs({ + activeDatasource: this.activeDataInfo.activeDatasource!, + activeDatasourceState: this.activeDataInfo.activeDatasourceState, + activeData: this.activeDataInfo.activeData, + indexPatterns: this.indexPatterns!, + capabilities: this.deps.capabilities, + query: this.externalSearchContext.query!, + filters: this.externalSearchContext.filters!, + timeRange: this.externalSearchContext.timeRange!, + }); - return { - indexPatternId: meta.id, - timeRange: this.input.timeRange, - filters: newFilters, - query: newQuery, - columns: meta.columns, - }; + console.log(viewUnderlyingDataArgs); } async initializeOutput() { if (!this.savedVis) { return; } - const responses = await Promise.allSettled( - uniqBy( - this.savedVis.references.filter(({ type }) => type === 'index-pattern'), - 'id' - ).map(({ id }) => this.deps.indexPatternService.get(id)) + + const { indexPatterns } = await getIndexPatternsObjects( + this.savedVis?.references.map(({ id }) => id) || [], + this.deps.indexPatternService ); - const indexPatterns = responses - .filter( - (response): response is PromiseFulfilledResult => - response.status === 'fulfilled' - ) - .map(({ value }) => value); + + this.indexPatterns = indexPatterns; // passing edit url and index patterns to the output of this embeddable for // the container to pick them up and use them to configure filter bar and // config dropdown correctly. const input = this.getInput(); - this.errors = this.maybeAddTimeRangeError(this.errors, input, indexPatterns); + this.errors = this.maybeAddTimeRangeError(this.errors, input, this.indexPatterns); if (this.errors) { this.logError('validation'); @@ -683,7 +726,7 @@ export class Embeddable title, editPath: getEditPath(savedObjectId), editUrl: this.deps.basePath.prepend(`/app/lens${getEditPath(savedObjectId)}`), - indexPatterns, + indexPatterns: this.indexPatterns, }); // deferred loading of this embeddable is complete From 52997f6357fce068943a5adf2b307483c4c0bfec Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 4 Mar 2022 15:22:34 -0600 Subject: [PATCH 003/140] working now --- .../lens/public/embeddable/embeddable.tsx | 30 +++++++++++++++++-- x-pack/plugins/lens/public/plugin.ts | 9 ++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 3344a033fb2ff..e0c6af5c7b354 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -21,7 +21,7 @@ import { import type { PaletteOutput } from 'src/plugins/charts/public'; import type { Start as InspectorStart } from 'src/plugins/inspector/public'; -import { Subscription } from 'rxjs'; +import { BehaviorSubject, Subscription } from 'rxjs'; import { toExpression, Ast } from '@kbn/interpreter'; import { RenderMode } from 'src/plugins/expressions'; import { map, distinctUntilChanged, skip } from 'rxjs/operators'; @@ -138,6 +138,14 @@ export interface LensEmbeddableDeps { theme: ThemeServiceStart; } +export interface ViewUnderlyingDataArgs { + indexPatternId: string; + timeRange: TimeRange; + filters: Filter[]; + query: Query | undefined; + columns: string[]; +} + const getExpressionFromDocument = async ( document: Document, documentToExpression: LensEmbeddableDeps['documentToExpression'] @@ -238,6 +246,10 @@ export class Embeddable private indexPatterns: IndexPattern[] = []; + private viewUnderlyingDataArgs$ = new BehaviorSubject( + undefined + ); + constructor( private deps: LensEmbeddableDeps, initialInput: LensEmbeddableInput, @@ -691,7 +703,21 @@ export class Embeddable timeRange: this.externalSearchContext.timeRange!, }); - console.log(viewUnderlyingDataArgs); + if (viewUnderlyingDataArgs) { + this.viewUnderlyingDataArgs$.next(viewUnderlyingDataArgs); + } + } + + /** + * Returns the necessary arguments to view the underlying data in discover. + */ + public getViewUnderlyingDataArgs() { + return new Promise((resolve) => { + const subscription = this.viewUnderlyingDataArgs$.subscribe((val) => { + resolve(val); + subscription.unsubscribe(); + }); + }); } async initializeOutput() { diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 9dfd8be1d5c46..2a6f7cef2a66f 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -92,7 +92,7 @@ import { getLensAliasConfig } from './vis_type_alias'; import { visualizeFieldAction } from './trigger_actions/visualize_field_actions'; import { visualizeTSVBAction } from './trigger_actions/visualize_tsvb_actions'; -import type { LensEmbeddableInput } from './embeddable'; +import type { Embeddable, LensEmbeddableInput } from './embeddable'; import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; import { EmbeddableComponentProps, @@ -452,8 +452,11 @@ export class LensPlugin { isCompatible: async (context: { embeddable: IEmbeddable }) => { return context.embeddable.type === DOC_TYPE; }, - execute: async (context: { embeddable: IEmbeddable }) => { - alert('FOOBAR'); + execute: async (context: { embeddable: Embeddable }) => { + const args = await context.embeddable.getViewUnderlyingDataArgs(); + startDependencies.discover?.locator?.navigate({ + ...args, + }); }, }) ); From 5792debe754edbb2c489f78220ec35591398a845 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 4 Mar 2022 16:35:21 -0600 Subject: [PATCH 004/140] Add compatibility check for action --- .../lens/public/embeddable/embeddable.tsx | 68 +++++++++++++------ x-pack/plugins/lens/public/plugin.ts | 9 ++- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index e0c6af5c7b354..27cde618e499e 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -21,7 +21,7 @@ import { import type { PaletteOutput } from 'src/plugins/charts/public'; import type { Start as InspectorStart } from 'src/plugins/inspector/public'; -import { BehaviorSubject, Subscription } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; import { toExpression, Ast } from '@kbn/interpreter'; import { RenderMode } from 'src/plugins/expressions'; import { map, distinctUntilChanged, skip } from 'rxjs/operators'; @@ -231,12 +231,12 @@ export class Embeddable ); } - private externalSearchContext: { + private externalSearchContext?: { timeRange?: TimeRange; query?: Query; filters?: Filter[]; searchSessionId?: string; - } = {}; + }; private activeDataInfo: { activeData?: TableInspectorAdapter; @@ -246,9 +246,10 @@ export class Embeddable private indexPatterns: IndexPattern[] = []; - private viewUnderlyingDataArgs$ = new BehaviorSubject( - undefined - ); + private viewUnderlyingDataArgs?: ViewUnderlyingDataArgs; + + private waitVUDArgsLoaded: Promise; + private markVUDArgsLoaded: (loaded: boolean) => void; constructor( private deps: LensEmbeddableDeps, @@ -262,6 +263,21 @@ export class Embeddable }, parent ); + + const _vudSubscription = new Subject(); + + this.markVUDArgsLoaded = (loaded: boolean) => { + _vudSubscription.next(loaded); + _vudSubscription.complete(); + }; + + this.waitVUDArgsLoaded = new Promise((resolve) => { + const subscription = _vudSubscription.subscribe((loaded) => { + resolve(loaded); + subscription.unsubscribe(); + }); + }); + this.lensInspector = getLensInspectorService(deps.inspector); this.expressionRenderer = deps.expressionRenderer; this.initializeSavedVis(initialInput) @@ -448,10 +464,10 @@ export class Embeddable ? containerState.filters.filter((filter) => !filter.meta.disabled) : undefined; if ( - !isEqual(containerState.timeRange, this.externalSearchContext.timeRange) || - !isEqual(containerState.query, this.externalSearchContext.query) || - !isEqual(cleanedFilters, this.externalSearchContext.filters) || - this.externalSearchContext.searchSessionId !== containerState.searchSessionId || + !isEqual(containerState.timeRange, this.externalSearchContext?.timeRange) || + !isEqual(containerState.query, this.externalSearchContext?.query) || + !isEqual(cleanedFilters, this.externalSearchContext?.filters) || + this.externalSearchContext?.searchSessionId !== containerState.searchSessionId || this.embeddableTitle !== this.getTitle() ) { this.externalSearchContext = { @@ -680,6 +696,13 @@ export class Embeddable } private async updateViewUnderlyingDataArgs() { + if (!this.activeDataInfo.activeData || !this.externalSearchContext) { + // This function is called both when activeData and externalSearchContext are loaded + // because we're not sure which one will load first. But, both must be loaded before + // we actually run this. + return; + } + const activeDatasourceId = getActiveDatasourceIdFromDoc(this.savedVis); if (activeDatasourceId) { this.activeDataInfo.activeDatasource = this.deps.datasourceMap[activeDatasourceId]; @@ -703,21 +726,28 @@ export class Embeddable timeRange: this.externalSearchContext.timeRange!, }); - if (viewUnderlyingDataArgs) { - this.viewUnderlyingDataArgs$.next(viewUnderlyingDataArgs); + const loaded = typeof viewUnderlyingDataArgs !== 'undefined'; + + if (loaded) { + this.viewUnderlyingDataArgs = viewUnderlyingDataArgs; } + + this.markVUDArgsLoaded(loaded); } /** * Returns the necessary arguments to view the underlying data in discover. */ - public getViewUnderlyingDataArgs() { - return new Promise((resolve) => { - const subscription = this.viewUnderlyingDataArgs$.subscribe((val) => { - resolve(val); - subscription.unsubscribe(); - }); - }); + public async getViewUnderlyingDataArgs() { + const loaded = await this.waitVUDArgsLoaded; + if (loaded) { + return this.viewUnderlyingDataArgs; + } + } + + public getCanViewUnderlyingData() { + // if loading the underlying data args fails + return this.waitVUDArgsLoaded; } async initializeOutput() { diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 2a6f7cef2a66f..0651f52783a01 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -92,7 +92,8 @@ import { getLensAliasConfig } from './vis_type_alias'; import { visualizeFieldAction } from './trigger_actions/visualize_field_actions'; import { visualizeTSVBAction } from './trigger_actions/visualize_tsvb_actions'; -import type { Embeddable, LensEmbeddableInput } from './embeddable'; +import type { LensEmbeddableInput } from './embeddable'; +import { Embeddable } from './embeddable'; import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; import { EmbeddableComponentProps, @@ -450,7 +451,11 @@ export class LensPlugin { id: 'VIEW_UNDERLYING_DATA', getDisplayName: () => 'Open in Discover', isCompatible: async (context: { embeddable: IEmbeddable }) => { - return context.embeddable.type === DOC_TYPE; + let isCompatible = false; + if (context.embeddable instanceof Embeddable) { + isCompatible = await context.embeddable.getCanViewUnderlyingData(); + } + return isCompatible; }, execute: async (context: { embeddable: Embeddable }) => { const args = await context.embeddable.getViewUnderlyingDataArgs(); From 9661db23266878ef6cf937e8d372e711ca603b14 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 4 Mar 2022 16:51:54 -0600 Subject: [PATCH 005/140] Move action creator to own module --- x-pack/plugins/lens/public/plugin.ts | 30 ++-------------- .../view_underlying_data_action.ts | 34 +++++++++++++++++++ 2 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 x-pack/plugins/lens/public/trigger_actions/view_underlying_data_action.ts diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 0651f52783a01..a2178da200277 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -20,7 +20,6 @@ import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart, - IEmbeddable, } from '../../../../src/plugins/embeddable/public'; import type { DashboardStart } from '../../../../src/plugins/dashboard/public'; import type { SpacesPluginStart } from '../../spaces/public'; @@ -72,15 +71,9 @@ import { UiActionsStart, ACTION_VISUALIZE_FIELD, VISUALIZE_FIELD_TRIGGER, - createAction, } from '../../../../src/plugins/ui_actions/public'; import { VISUALIZE_EDITOR_TRIGGER } from '../../../../src/plugins/visualizations/public'; -import { - APP_ID, - DOC_TYPE, - getEditPath, - NOT_INTERNATIONALIZED_PRODUCT_NAME, -} from '../common/constants'; +import { APP_ID, getEditPath, NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common/constants'; import type { FormatFactory } from '../common/types'; import type { Visualization, @@ -89,11 +82,11 @@ import type { LensTopNavMenuEntryGenerator, } from './types'; import { getLensAliasConfig } from './vis_type_alias'; +import { viewUnderlyingDataAction } from './trigger_actions/view_underlying_data_action'; import { visualizeFieldAction } from './trigger_actions/visualize_field_actions'; import { visualizeTSVBAction } from './trigger_actions/visualize_tsvb_actions'; import type { LensEmbeddableInput } from './embeddable'; -import { Embeddable } from './embeddable'; import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; import { EmbeddableComponentProps, @@ -446,24 +439,7 @@ export class LensPlugin { startDependencies.uiActions.addTriggerAction( CONTEXT_MENU_TRIGGER, - createAction<{ embeddable: IEmbeddable }>({ - type: 'VIEW_UNDERLYING_DATA', - id: 'VIEW_UNDERLYING_DATA', - getDisplayName: () => 'Open in Discover', - isCompatible: async (context: { embeddable: IEmbeddable }) => { - let isCompatible = false; - if (context.embeddable instanceof Embeddable) { - isCompatible = await context.embeddable.getCanViewUnderlyingData(); - } - return isCompatible; - }, - execute: async (context: { embeddable: Embeddable }) => { - const args = await context.embeddable.getViewUnderlyingDataArgs(); - startDependencies.discover?.locator?.navigate({ - ...args, - }); - }, - }) + viewUnderlyingDataAction(startDependencies.discover!) ); return { diff --git a/x-pack/plugins/lens/public/trigger_actions/view_underlying_data_action.ts b/x-pack/plugins/lens/public/trigger_actions/view_underlying_data_action.ts new file mode 100644 index 0000000000000..54624392a3dbc --- /dev/null +++ b/x-pack/plugins/lens/public/trigger_actions/view_underlying_data_action.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { IEmbeddable } from 'src/plugins/embeddable/public'; +import { createAction } from '../../../../../src/plugins/ui_actions/public'; +import { Embeddable } from '../embeddable'; +import type { DiscoverStart } from '../../../../../src/plugins/discover/public'; + +const ACTION_VIEW_UNDERLYING_DATA = 'ACTION_VIEW_UNDERLYING_DATA'; + +export const viewUnderlyingDataAction = (discover: DiscoverStart) => + createAction<{ embeddable: IEmbeddable }>({ + type: ACTION_VIEW_UNDERLYING_DATA, + id: ACTION_VIEW_UNDERLYING_DATA, + getDisplayName: () => 'Open in Discover', + isCompatible: async (context: { embeddable: IEmbeddable }) => { + let isCompatible = false; + if (context.embeddable instanceof Embeddable) { + isCompatible = await context.embeddable.getCanViewUnderlyingData(); + } + return isCompatible; + }, + execute: async (context: { embeddable: Embeddable }) => { + const args = await context.embeddable.getViewUnderlyingDataArgs(); + discover.locator?.navigate({ + ...args, + }); + }, + }); From 7c6d314cb0e91cde28db1c24b0acff21e447d839 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Mon, 7 Mar 2022 10:47:18 +0000 Subject: [PATCH 006/140] [Fleet] Retry Saved Object import on conflict error (#126900) * retry SO import on conflict errors * add jitter + increase retries * Apply suggestions from code review Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com> Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com> --- .../epm/kibana/assets/install.test.ts | 124 ++++++++++++++++++ .../services/epm/kibana/assets/install.ts | 51 +++++-- 2 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/fleet/server/services/epm/kibana/assets/install.test.ts diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.test.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.test.ts new file mode 100644 index 0000000000000..51aee45c83cf3 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.test.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { + ISavedObjectsImporter, + SavedObjectsImportFailure, + SavedObjectsImportSuccess, + SavedObjectsImportResponse, +} from 'src/core/server'; + +import { loggingSystemMock } from '../../../../../../../../src/core/server/mocks'; + +import type { ArchiveAsset } from './install'; + +jest.mock('timers/promises', () => ({ + async setTimeout() {}, +})); + +import { installKibanaSavedObjects } from './install'; + +const mockLogger = loggingSystemMock.createLogger(); + +const mockImporter: jest.Mocked = { + import: jest.fn(), + resolveImportErrors: jest.fn(), +}; + +const createImportError = (so: ArchiveAsset, type: string) => + ({ id: so.id, error: { type } } as SavedObjectsImportFailure); +const createImportSuccess = (so: ArchiveAsset) => + ({ id: so.id, type: so.type, meta: {} } as SavedObjectsImportSuccess); +const createAsset = (asset: Partial) => + ({ id: 1234, type: 'dashboard', attributes: {}, ...asset } as ArchiveAsset); + +const createImportResponse = ( + errors: SavedObjectsImportFailure[] = [], + successResults: SavedObjectsImportSuccess[] = [] +) => + ({ + success: !!successResults.length, + errors, + successResults, + warnings: [], + successCount: successResults.length, + } as SavedObjectsImportResponse); + +describe('installKibanaSavedObjects', () => { + beforeEach(() => { + mockImporter.import.mockReset(); + mockImporter.resolveImportErrors.mockReset(); + }); + + it('should retry on conflict error', async () => { + const asset = createAsset({ attributes: { hello: 'world' } }); + const conflictResponse = createImportResponse([createImportError(asset, 'conflict')]); + const successResponse = createImportResponse([], [createImportSuccess(asset)]); + + mockImporter.import + .mockResolvedValueOnce(conflictResponse) + .mockResolvedValueOnce(successResponse); + + await installKibanaSavedObjects({ + savedObjectsImporter: mockImporter, + logger: mockLogger, + kibanaAssets: [asset], + }); + + expect(mockImporter.import).toHaveBeenCalledTimes(2); + }); + + it('should give up after 50 retries on conflict errors', async () => { + const asset = createAsset({ attributes: { hello: 'world' } }); + const conflictResponse = createImportResponse([createImportError(asset, 'conflict')]); + + mockImporter.import.mockImplementation(() => Promise.resolve(conflictResponse)); + + await expect( + installKibanaSavedObjects({ + savedObjectsImporter: mockImporter, + logger: mockLogger, + kibanaAssets: [asset], + }) + ).rejects.toEqual(expect.any(Error)); + expect(mockImporter.import).toHaveBeenCalledTimes(51); + }); + it('should not retry errors that arent conflict errors', async () => { + const asset = createAsset({ attributes: { hello: 'world' } }); + const errorResponse = createImportResponse([createImportError(asset, 'something_bad')]); + const successResponse = createImportResponse([], [createImportSuccess(asset)]); + + mockImporter.import.mockResolvedValueOnce(errorResponse).mockResolvedValueOnce(successResponse); + + expect( + installKibanaSavedObjects({ + savedObjectsImporter: mockImporter, + logger: mockLogger, + kibanaAssets: [asset], + }) + ).rejects.toEqual(expect.any(Error)); + }); + + it('should resolve reference errors', async () => { + const asset = createAsset({ attributes: { hello: 'world' } }); + const referenceErrorResponse = createImportResponse([ + createImportError(asset, 'missing_references'), + ]); + const successResponse = createImportResponse([], [createImportSuccess(asset)]); + + mockImporter.import.mockResolvedValueOnce(referenceErrorResponse); + mockImporter.resolveImportErrors.mockResolvedValueOnce(successResponse); + + await installKibanaSavedObjects({ + savedObjectsImporter: mockImporter, + logger: mockLogger, + kibanaAssets: [asset], + }); + + expect(mockImporter.import).toHaveBeenCalledTimes(1); + expect(mockImporter.resolveImportErrors).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index 5ab15a1f52e75..d654fab427f19 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { setTimeout } from 'timers/promises'; + import type { SavedObject, SavedObjectsBulkCreateObject, @@ -13,7 +15,6 @@ import type { Logger, } from 'src/core/server'; import type { SavedObjectsImportSuccess, SavedObjectsImportFailure } from 'src/core/server/types'; - import { createListStream } from '@kbn/utils'; import { partition } from 'lodash'; @@ -166,7 +167,40 @@ export async function getKibanaAssets( return result; } -async function installKibanaSavedObjects({ +const isImportConflictError = (e: SavedObjectsImportFailure) => e?.error?.type === 'conflict'; +/** + * retry saved object import if only conflict errors are encountered + */ +async function retryImportOnConflictError( + importCall: () => ReturnType, + { + logger, + maxAttempts = 50, + _attempt = 0, + }: { logger?: Logger; _attempt?: number; maxAttempts?: number } = {} +): ReturnType { + const result = await importCall(); + + const errors = result.errors ?? []; + if (_attempt < maxAttempts && errors.length && errors.every(isImportConflictError)) { + const retryCount = _attempt + 1; + const retryDelayMs = 1000 + Math.floor(Math.random() * 3000); // 1s + 0-3s of jitter + + logger?.debug( + `Retrying import operation after [${ + retryDelayMs * 1000 + }s] due to conflict errors: ${JSON.stringify(errors)}` + ); + + await setTimeout(retryDelayMs); + return retryImportOnConflictError(importCall, { logger, _attempt: retryCount }); + } + + return result; +} + +// only exported for testing +export async function installKibanaSavedObjects({ savedObjectsImporter, kibanaAssets, logger, @@ -185,18 +219,19 @@ async function installKibanaSavedObjects({ return []; } else { const { successResults: importSuccessResults = [], errors: importErrors = [] } = - await savedObjectsImporter.import({ - overwrite: true, - readStream: createListStream(toBeSavedObjects), - createNewCopies: false, - }); + await retryImportOnConflictError(() => + savedObjectsImporter.import({ + overwrite: true, + readStream: createListStream(toBeSavedObjects), + createNewCopies: false, + }) + ); allSuccessResults = importSuccessResults; const [referenceErrors, otherErrors] = partition( importErrors, (e) => e?.error?.type === 'missing_references' ); - if (otherErrors?.length) { throw new Error( `Encountered ${ From 3c9014737a9791bd9365b1fd0f880a5b5d8bdacf Mon Sep 17 00:00:00 2001 From: Muhammad Ibragimov <53621505+mibragimov@users.noreply.github.com> Date: Mon, 7 Mar 2022 16:14:31 +0500 Subject: [PATCH 007/140] [Console] Support auto-complete for data streams (#126235) * Support auto-complete for data streams * Add a test case Co-authored-by: Muhammad Ibragimov Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/components/settings_modal.tsx | 11 +++++++++ .../data_stream_autocomplete_component.js | 20 ++++++++++++++++ .../lib/autocomplete/components/index.js | 1 + src/plugins/console/public/lib/kb/kb.js | 4 ++++ .../public/lib/mappings/mapping.test.js | 9 +++++++ .../console/public/lib/mappings/mappings.js | 24 ++++++++++++++++--- .../console/public/services/settings.ts | 3 ++- .../generated/indices.delete_data_stream.json | 2 +- .../generated/indices.get_data_stream.json | 3 ++- 9 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 src/plugins/console/public/lib/autocomplete/components/data_stream_autocomplete_component.js diff --git a/src/plugins/console/public/application/components/settings_modal.tsx b/src/plugins/console/public/application/components/settings_modal.tsx index c4be329dabcb8..eafc2dea3f873 100644 --- a/src/plugins/console/public/application/components/settings_modal.tsx +++ b/src/plugins/console/public/application/components/settings_modal.tsx @@ -70,6 +70,7 @@ export function DevToolsSettingsModal(props: Props) { const [fields, setFields] = useState(props.settings.autocomplete.fields); const [indices, setIndices] = useState(props.settings.autocomplete.indices); const [templates, setTemplates] = useState(props.settings.autocomplete.templates); + const [dataStreams, setDataStreams] = useState(props.settings.autocomplete.dataStreams); const [polling, setPolling] = useState(props.settings.polling); const [pollInterval, setPollInterval] = useState(props.settings.pollInterval); const [tripleQuotes, setTripleQuotes] = useState(props.settings.tripleQuotes); @@ -97,12 +98,20 @@ export function DevToolsSettingsModal(props: Props) { }), stateSetter: setTemplates, }, + { + id: 'dataStreams', + label: i18n.translate('console.settingsPage.dataStreamsLabelText', { + defaultMessage: 'Data streams', + }), + stateSetter: setDataStreams, + }, ]; const checkboxIdToSelectedMap = { fields, indices, templates, + dataStreams, }; const onAutocompleteChange = (optionId: AutocompleteOptions) => { @@ -120,6 +129,7 @@ export function DevToolsSettingsModal(props: Props) { fields, indices, templates, + dataStreams, }, polling, pollInterval, @@ -170,6 +180,7 @@ export function DevToolsSettingsModal(props: Props) { fields, indices, templates, + dataStreams, }); }} > diff --git a/src/plugins/console/public/lib/autocomplete/components/data_stream_autocomplete_component.js b/src/plugins/console/public/lib/autocomplete/components/data_stream_autocomplete_component.js new file mode 100644 index 0000000000000..015136b7670f5 --- /dev/null +++ b/src/plugins/console/public/lib/autocomplete/components/data_stream_autocomplete_component.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getDataStreams } from '../../mappings/mappings'; +import { ListComponent } from './list_component'; + +export class DataStreamAutocompleteComponent extends ListComponent { + constructor(name, parent, multiValued) { + super(name, getDataStreams, parent, multiValued); + } + + getContextKey() { + return 'data_stream'; + } +} diff --git a/src/plugins/console/public/lib/autocomplete/components/index.js b/src/plugins/console/public/lib/autocomplete/components/index.js index 32078ee2c1519..4a8838a6fb821 100644 --- a/src/plugins/console/public/lib/autocomplete/components/index.js +++ b/src/plugins/console/public/lib/autocomplete/components/index.js @@ -23,4 +23,5 @@ export { IdAutocompleteComponent } from './id_autocomplete_component'; export { UsernameAutocompleteComponent } from './username_autocomplete_component'; export { IndexTemplateAutocompleteComponent } from './index_template_autocomplete_component'; export { ComponentTemplateAutocompleteComponent } from './component_template_autocomplete_component'; +export { DataStreamAutocompleteComponent } from './data_stream_autocomplete_component'; export * from './legacy'; diff --git a/src/plugins/console/public/lib/kb/kb.js b/src/plugins/console/public/lib/kb/kb.js index 5f02365a48fdf..e268f55be558e 100644 --- a/src/plugins/console/public/lib/kb/kb.js +++ b/src/plugins/console/public/lib/kb/kb.js @@ -16,6 +16,7 @@ import { UsernameAutocompleteComponent, IndexTemplateAutocompleteComponent, ComponentTemplateAutocompleteComponent, + DataStreamAutocompleteComponent, } from '../autocomplete/components'; import $ from 'jquery'; @@ -94,6 +95,9 @@ const parametrizedComponentFactories = { component_template: function (name, parent) { return new ComponentTemplateAutocompleteComponent(name, parent); }, + data_stream: function (name, parent) { + return new DataStreamAutocompleteComponent(name, parent); + }, }; export function getUnmatchedEndpointComponents() { diff --git a/src/plugins/console/public/lib/mappings/mapping.test.js b/src/plugins/console/public/lib/mappings/mapping.test.js index 9191eb736be3c..e2def74e892cc 100644 --- a/src/plugins/console/public/lib/mappings/mapping.test.js +++ b/src/plugins/console/public/lib/mappings/mapping.test.js @@ -266,4 +266,13 @@ describe('Mappings', () => { expect(mappings.getIndexTemplates()).toEqual(expectedResult); expect(mappings.getComponentTemplates()).toEqual(expectedResult); }); + + test('Data streams', function () { + mappings.loadDataStreams({ + data_streams: [{ name: 'test_index1' }, { name: 'test_index2' }, { name: 'test_index3' }], + }); + + const expectedResult = ['test_index1', 'test_index2', 'test_index3']; + expect(mappings.getDataStreams()).toEqual(expectedResult); + }); }); diff --git a/src/plugins/console/public/lib/mappings/mappings.js b/src/plugins/console/public/lib/mappings/mappings.js index 75b8a263e8690..96a5665e730a2 100644 --- a/src/plugins/console/public/lib/mappings/mappings.js +++ b/src/plugins/console/public/lib/mappings/mappings.js @@ -17,6 +17,7 @@ let perAliasIndexes = []; let legacyTemplates = []; let indexTemplates = []; let componentTemplates = []; +let dataStreams = []; const mappingObj = {}; @@ -60,6 +61,10 @@ export function getComponentTemplates() { return [...componentTemplates]; } +export function getDataStreams() { + return [...dataStreams]; +} + export function getFields(indices, types) { // get fields for indices and types. Both can be a list, a string or null (meaning all). let ret = []; @@ -128,7 +133,9 @@ export function getTypes(indices) { export function getIndices(includeAliases) { const ret = []; $.each(perIndexTypes, function (index) { - ret.push(index); + if (!index.startsWith('.ds')) { + ret.push(index); + } }); if (typeof includeAliases === 'undefined' ? true : includeAliases) { $.each(perAliasIndexes, function (alias) { @@ -204,6 +211,10 @@ export function loadComponentTemplates(data) { componentTemplates = (data.component_templates ?? []).map(({ name }) => name); } +export function loadDataStreams(data) { + dataStreams = (data.data_streams ?? []).map(({ name }) => name); +} + export function loadMappings(mappings) { perIndexTypes = {}; @@ -265,6 +276,7 @@ function retrieveSettings(settingsKey, settingsToRetrieve) { legacyTemplates: '_template', indexTemplates: '_index_template', componentTemplates: '_component_template', + dataStreams: '_data_stream', }; // Fetch autocomplete info if setting is set to true, and if user has made changes. @@ -326,14 +338,16 @@ export function retrieveAutoCompleteInfo(settings, settingsToRetrieve) { 'componentTemplates', templatesSettingToRetrieve ); + const dataStreamsPromise = retrieveSettings('dataStreams', settingsToRetrieve); $.when( mappingPromise, aliasesPromise, legacyTemplatesPromise, indexTemplatesPromise, - componentTemplatesPromise - ).done((mappings, aliases, legacyTemplates, indexTemplates, componentTemplates) => { + componentTemplatesPromise, + dataStreamsPromise + ).done((mappings, aliases, legacyTemplates, indexTemplates, componentTemplates, dataStreams) => { let mappingsResponse; try { if (mappings && mappings.length) { @@ -365,6 +379,10 @@ export function retrieveAutoCompleteInfo(settings, settingsToRetrieve) { loadComponentTemplates(JSON.parse(componentTemplates[0])); } + if (dataStreams) { + loadDataStreams(JSON.parse(dataStreams[0])); + } + if (mappings && aliases) { // Trigger an update event with the mappings, aliases $(mappingObj).trigger('update', [mappingsResponse, aliases[0]]); diff --git a/src/plugins/console/public/services/settings.ts b/src/plugins/console/public/services/settings.ts index 058f6c20c1888..1a7eff3e7ca54 100644 --- a/src/plugins/console/public/services/settings.ts +++ b/src/plugins/console/public/services/settings.ts @@ -14,7 +14,7 @@ export const DEFAULT_SETTINGS = Object.freeze({ pollInterval: 60000, tripleQuotes: true, wrapMode: true, - autocomplete: Object.freeze({ fields: true, indices: true, templates: true }), + autocomplete: Object.freeze({ fields: true, indices: true, templates: true, dataStreams: true }), historyDisabled: false, }); @@ -25,6 +25,7 @@ export interface DevToolsSettings { fields: boolean; indices: boolean; templates: boolean; + dataStreams: boolean; }; polling: boolean; pollInterval: number; diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_data_stream.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_data_stream.json index 9b91e3deb3a08..fb5cb446fb77e 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_data_stream.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_data_stream.json @@ -13,7 +13,7 @@ "DELETE" ], "patterns": [ - "_data_stream/{name}" + "_data_stream/{data_stream}" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html" } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_data_stream.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_data_stream.json index 45199a60f337d..e383a1df4844a 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_data_stream.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_data_stream.json @@ -14,7 +14,8 @@ ], "patterns": [ "_data_stream", - "_data_stream/{name}" + "_data_stream/{name}", + "{data_stream}" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/data-streams.html" } From c9e66b8327d3d3087bcd1788aed121977515fac0 Mon Sep 17 00:00:00 2001 From: CohenIdo <90558359+CohenIdo@users.noreply.github.com> Date: Mon, 7 Mar 2022 14:31:42 +0200 Subject: [PATCH 008/140] [Cloud Posture] add filterting for benchmark (#126980) --- .../routes/benchmarks/benchmarks.test.ts | 30 +++++++++++++++++++ .../server/routes/benchmarks/benchmarks.ts | 15 ++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts index b728948cf2a05..8c9d04dc207f3 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts @@ -76,6 +76,18 @@ describe('benchmarks API', () => { }); }); + it('expect to find benchmark_name', async () => { + const validatedQuery = benchmarksInputSchema.validate({ + benchmark_name: 'my_cis_benchmark', + }); + + expect(validatedQuery).toMatchObject({ + page: 1, + per_page: DEFAULT_BENCHMARKS_PER_PAGE, + benchmark_name: 'my_cis_benchmark', + }); + }); + it('should throw when page field is not a positive integer', async () => { expect(() => { benchmarksInputSchema.validate({ page: -2 }); @@ -125,6 +137,24 @@ describe('benchmarks API', () => { }); }); + it('should format request by benchmark_name', async () => { + const mockAgentPolicyService = createPackagePolicyServiceMock(); + + await getPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { + page: 1, + per_page: 100, + benchmark_name: 'my_cis_benchmark', + }); + + expect(mockAgentPolicyService.list.mock.calls[0][1]).toMatchObject( + expect.objectContaining({ + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:myPackage AND ${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: *my_cis_benchmark*`, + page: 1, + perPage: 100, + }) + ); + }); + describe('test getAgentPolicies', () => { it('should return one agent policy id when there is duplication', async () => { const agentPolicyService = createMockAgentPolicyService(); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts index 80c526c248c0f..c52aeead6cd4d 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts @@ -43,8 +43,13 @@ export interface Benchmark { export const DEFAULT_BENCHMARKS_PER_PAGE = 20; export const PACKAGE_POLICY_SAVED_OBJECT_TYPE = 'ingest-package-policies'; -const getPackageNameQuery = (packageName: string): string => { - return `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${packageName}`; +const getPackageNameQuery = (packageName: string, benchmarkFilter?: string): string => { + const integrationNameQuery = `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${packageName}`; + const kquery = benchmarkFilter + ? `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${packageName} AND ${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: *${benchmarkFilter}*` + : integrationNameQuery; + + return kquery; }; export const getPackagePolicies = async ( @@ -57,7 +62,7 @@ export const getPackagePolicies = async ( throw new Error('packagePolicyService is undefined'); } - const packageNameQuery = getPackageNameQuery(packageName); + const packageNameQuery = getPackageNameQuery(packageName, queryParams.benchmark_name); const { items: packagePolicies } = (await packagePolicyService?.list(soClient, { kuery: packageNameQuery, @@ -193,4 +198,8 @@ export const benchmarksInputSchema = rt.object({ * The number of objects to include in each page */ per_page: rt.number({ defaultValue: DEFAULT_BENCHMARKS_PER_PAGE, min: 0 }), + /** + * Benchmark filter + */ + benchmark_name: rt.maybe(rt.string()), }); From bc0d9e70791be8dfdeec9770625950f884fb50e8 Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Mon, 7 Mar 2022 08:13:52 -0500 Subject: [PATCH 009/140] [Presentation] Fix some bugs with services dependency injection (#126936) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../create/dependency_manager.test.ts | 54 +++++++++++++------ .../services/create/dependency_manager.ts | 50 +++++++++++------ 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/plugins/presentation_util/public/services/create/dependency_manager.test.ts b/src/plugins/presentation_util/public/services/create/dependency_manager.test.ts index 29702c3356865..8e67dee3f8b6b 100644 --- a/src/plugins/presentation_util/public/services/create/dependency_manager.test.ts +++ b/src/plugins/presentation_util/public/services/create/dependency_manager.test.ts @@ -24,6 +24,16 @@ describe('DependencyManager', () => { expect(DependencyManager.orderDependencies(graph)).toEqual(sortedTopology); }); + it('should include final vertex if it has dependencies', () => { + const graph = { + A: [], + B: [], + C: ['A', 'B'], + }; + const sortedTopology = ['A', 'B', 'C']; + expect(DependencyManager.orderDependencies(graph)).toEqual(sortedTopology); + }); + it('orderDependencies. Should return base topology if no depended vertices', () => { const graph = { N: [], @@ -34,22 +44,34 @@ describe('DependencyManager', () => { expect(DependencyManager.orderDependencies(graph)).toEqual(sortedTopology); }); - it('orderDependencies. Should detect circular dependencies and throw error with path', () => { - const graph = { - N: ['R'], - R: ['A'], - A: ['B'], - B: ['C'], - C: ['D'], - D: ['E'], - E: ['F'], - F: ['L'], - L: ['G'], - G: ['N'], - }; - const circularPath = ['N', 'R', 'A', 'B', 'C', 'D', 'E', 'F', 'L', 'G', 'N'].join(' -> '); - const errorMessage = `Circular dependency detected while setting up services: ${circularPath}`; + describe('circular dependencies', () => { + it('should detect circular dependencies and throw error with path', () => { + const graph = { + N: ['R'], + R: ['A'], + A: ['B'], + B: ['C'], + C: ['D'], + D: ['E'], + E: ['F'], + F: ['L'], + L: ['G'], + G: ['N'], + }; + const circularPath = ['G', 'L', 'F', 'E', 'D', 'C', 'B', 'A', 'R', 'N'].join(' -> '); + const errorMessage = `Circular dependency detected while setting up services: ${circularPath}`; + + expect(() => DependencyManager.orderDependencies(graph)).toThrowError(errorMessage); + }); + + it('should detect circular dependency if circular reference is the first dependency for a vertex', () => { + const graph = { + A: ['B'], + B: ['A', 'C'], + C: [], + }; - expect(() => DependencyManager.orderDependencies(graph)).toThrowError(errorMessage); + expect(() => DependencyManager.orderDependencies(graph)).toThrow(); + }); }); }); diff --git a/src/plugins/presentation_util/public/services/create/dependency_manager.ts b/src/plugins/presentation_util/public/services/create/dependency_manager.ts index de30b180607fe..3925f3e9d9c4f 100644 --- a/src/plugins/presentation_util/public/services/create/dependency_manager.ts +++ b/src/plugins/presentation_util/public/services/create/dependency_manager.ts @@ -41,7 +41,14 @@ export class DependencyManager { return cycleInfo; } - return DependencyManager.sortVerticesFrom(srcVertex, graph, sortedVertices, {}, {}); + return DependencyManager.sortVerticesFrom( + srcVertex, + graph, + sortedVertices, + {}, + {}, + cycleInfo + ); }, DependencyManager.createCycleInfo()); } @@ -58,24 +65,30 @@ export class DependencyManager { graph: Graph, sortedVertices: Set, visited: BreadCrumbs = {}, - inpath: BreadCrumbs = {} + inpath: BreadCrumbs = {}, + cycle: CycleDetectionResult ): CycleDetectionResult { visited[srcVertex] = true; inpath[srcVertex] = true; - const cycleInfo = graph[srcVertex]?.reduce | undefined>( - (info, vertex) => { - if (inpath[vertex]) { - const path = (Object.keys(inpath) as T[]).filter( - (visitedVertex) => inpath[visitedVertex] - ); - return DependencyManager.createCycleInfo([...path, vertex], true); - } else if (!visited[vertex]) { - return DependencyManager.sortVerticesFrom(vertex, graph, sortedVertices, visited, inpath); - } - return info; - }, - undefined - ); + + const vertexEdges = + graph[srcVertex] === undefined || graph[srcVertex] === null ? [] : graph[srcVertex]; + + cycle = vertexEdges!.reduce>((info, vertex) => { + if (inpath[vertex]) { + return { ...info, hasCycle: true }; + } else if (!visited[vertex]) { + return DependencyManager.sortVerticesFrom( + vertex, + graph, + sortedVertices, + visited, + inpath, + info + ); + } + return info; + }, cycle); inpath[srcVertex] = false; @@ -83,7 +96,10 @@ export class DependencyManager { sortedVertices.add(srcVertex); } - return cycleInfo ?? DependencyManager.createCycleInfo([...sortedVertices]); + return { + ...cycle, + path: [...sortedVertices], + }; } private static createCycleInfo( From f12891ee460536a9c7dbd1848290f0cb0d429502 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Mon, 7 Mar 2022 14:14:13 +0100 Subject: [PATCH 010/140] :bug: Handle case of undefined fitting for line/area (#126891) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/vis_types/xy/public/config/get_config.ts | 4 ++-- .../options/point_series/elastic_charts_options.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/vis_types/xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts index d7cf22625e10e..7aad30c5b743e 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { ScaleContinuousType } from '@elastic/charts'; +import { Fit, ScaleContinuousType } from '@elastic/charts'; import { Datatable } from '../../../../expressions/public'; import { BUCKET_TYPES } from '../../../../data/public'; @@ -92,7 +92,7 @@ export function getConfig( return { // NOTE: downscale ratio to match current vislib implementation markSizeRatio: radiusRatio * 0.6, - fittingFunction, + fittingFunction: fittingFunction ?? Fit.Linear, fillOpacity, detailedTooltip, orderBucketsBySum, diff --git a/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx b/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx index 105cd66799041..1c93fe92b79af 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/point_series/elastic_charts_options.tsx @@ -78,7 +78,7 @@ export function ElasticChartsOptions(props: ValidationVisOptionsProps })} options={fittingFunctions} paramName="fittingFunction" - value={stateParams.fittingFunction} + value={stateParams.fittingFunction ?? fittingFunctions[2].value} setValue={(paramName, value) => { if (trackUiMetric) { trackUiMetric(METRIC_TYPE.CLICK, 'fitting_function_selected'); From 0807b53d75f663db4fb8508e4aa729aaba2ad792 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 7 Mar 2022 07:23:35 -0600 Subject: [PATCH 011/140] fix data view load err msg (#126974) --- src/plugins/data_views/common/data_views/data_views.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index 2e31ed793c3db..04c1fd98a0f60 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -424,7 +424,7 @@ export class DataViewsService { ); if (!savedObject.version) { - throw new SavedObjectNotFound(DATA_VIEW_SAVED_OBJECT_TYPE, id, 'management/kibana/dataViews'); + throw new SavedObjectNotFound('data view', id, 'management/kibana/dataViews'); } return this.initFromSavedObject(savedObject); From 0adb328a9af39d35c02442198a74476ba86d9687 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 7 Mar 2022 07:24:14 -0600 Subject: [PATCH 012/140] [data views] Reenable data view validation functional test (#125892) * reenable test --- ...e_delete.js => _index_pattern_create_delete.ts} | 14 ++++++++------ test/functional/page_objects/settings_page.ts | 13 +++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) rename test/functional/apps/management/{_index_pattern_create_delete.js => _index_pattern_create_delete.ts} (91%) diff --git a/test/functional/apps/management/_index_pattern_create_delete.js b/test/functional/apps/management/_index_pattern_create_delete.ts similarity index 91% rename from test/functional/apps/management/_index_pattern_create_delete.js rename to test/functional/apps/management/_index_pattern_create_delete.ts index 4c9f5a5210ac6..6b2036499a1ed 100644 --- a/test/functional/apps/management/_index_pattern_create_delete.js +++ b/test/functional/apps/management/_index_pattern_create_delete.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const browser = getService('browser'); @@ -35,8 +36,7 @@ export default function ({ getService, getPageObjects }) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/124663 - describe.skip('validation', function () { + describe('validation', function () { it('can display errors', async function () { await PageObjects.settings.clickAddNewIndexPatternButton(); await PageObjects.settings.setIndexPatternField('log-fake*'); @@ -46,7 +46,7 @@ export default function ({ getService, getPageObjects }) { it('can resolve errors and submit', async function () { await PageObjects.settings.setIndexPatternField('log*'); - await (await PageObjects.settings.getSaveIndexPatternButton()).click(); + await (await PageObjects.settings.getSaveDataViewButtonActive()).click(); await PageObjects.settings.removeIndexPattern(); }); }); @@ -72,10 +72,12 @@ export default function ({ getService, getPageObjects }) { }); describe('index pattern creation', function indexPatternCreation() { - let indexPatternId; + let indexPatternId: string; before(function () { - return PageObjects.settings.createIndexPattern().then((id) => (indexPatternId = id)); + return PageObjects.settings + .createIndexPattern('logstash-*') + .then((id) => (indexPatternId = id)); }); it('should have index pattern in page header', async function () { diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index 70cdbea7fa897..9c0fc73a23675 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -164,6 +164,19 @@ export class SettingsPageObject extends FtrService { return await this.testSubjects.find('saveIndexPatternButton'); } + async getSaveDataViewButtonActive() { + await this.retry.try(async () => { + expect( + ( + await this.find.allByCssSelector( + '[data-test-subj="saveIndexPatternButton"]:not(.euiButton-isDisabled)' + ) + ).length + ).to.be(1); + }); + return await this.testSubjects.find('saveIndexPatternButton'); + } + async getCreateButton() { return await this.find.displayedByCssSelector('[type="submit"]'); } From 8b82657d46e18920e3a1acc3133f10f78e25a6f4 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Mon, 7 Mar 2022 07:24:39 -0600 Subject: [PATCH 013/140] [data views] functional tests to typescript (#126977) js => ts --- ...ard.js => _create_index_pattern_wizard.ts} | 6 ++-- ...x_pattern.js => _exclude_index_pattern.ts} | 3 +- .../{_handle_alias.js => _handle_alias.ts} | 3 +- ...onflict.js => _handle_version_conflict.ts} | 4 +-- ...ern_filter.js => _index_pattern_filter.ts} | 5 +-- ...larity.js => _index_pattern_popularity.ts} | 5 +-- ...sort.js => _index_pattern_results_sort.ts} | 10 +++--- ...kibana_settings.js => _kibana_settings.ts} | 3 +- ...jects.js => _mgmt_import_saved_objects.ts} | 9 ++--- ...{_runtime_fields.js => _runtime_fields.ts} | 9 +++-- ...scripted_fields.js => _scripted_fields.ts} | 35 ++++++++++--------- ...s_filter.js => _scripted_fields_filter.ts} | 3 +- ...preview.js => _scripted_fields_preview.ts} | 9 ++--- ...st_huge_fields.js => _test_huge_fields.ts} | 5 +-- test/functional/page_objects/settings_page.ts | 6 ++-- 15 files changed, 65 insertions(+), 50 deletions(-) rename test/functional/apps/management/{_create_index_pattern_wizard.js => _create_index_pattern_wizard.ts} (93%) rename test/functional/apps/management/{_exclude_index_pattern.js => _exclude_index_pattern.ts} (89%) rename test/functional/apps/management/{_handle_alias.js => _handle_alias.ts} (95%) rename test/functional/apps/management/{_handle_version_conflict.js => _handle_version_conflict.ts} (96%) rename test/functional/apps/management/{_index_pattern_filter.js => _index_pattern_filter.ts} (90%) rename test/functional/apps/management/{_index_pattern_popularity.js => _index_pattern_popularity.ts} (92%) rename test/functional/apps/management/{_index_pattern_results_sort.js => _index_pattern_results_sort.ts} (90%) rename test/functional/apps/management/{_kibana_settings.js => _kibana_settings.ts} (96%) rename test/functional/apps/management/{_mgmt_import_saved_objects.js => _mgmt_import_saved_objects.ts} (80%) rename test/functional/apps/management/{_runtime_fields.js => _runtime_fields.ts} (91%) rename test/functional/apps/management/{_scripted_fields.js => _scripted_fields.ts} (96%) rename test/functional/apps/management/{_scripted_fields_filter.js => _scripted_fields_filter.ts} (95%) rename test/functional/apps/management/{_scripted_fields_preview.js => _scripted_fields_preview.ts} (90%) rename test/functional/apps/management/{_test_huge_fields.js => _test_huge_fields.ts} (90%) diff --git a/test/functional/apps/management/_create_index_pattern_wizard.js b/test/functional/apps/management/_create_index_pattern_wizard.ts similarity index 93% rename from test/functional/apps/management/_create_index_pattern_wizard.js rename to test/functional/apps/management/_create_index_pattern_wizard.ts index b2f24e530cb12..cf732e178aa74 100644 --- a/test/functional/apps/management/_create_index_pattern_wizard.js +++ b/test/functional/apps/management/_create_index_pattern_wizard.ts @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -export default function ({ getService, getPageObjects }) { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); const es = getService('es'); @@ -38,7 +40,7 @@ export default function ({ getService, getPageObjects }) { body: { actions: [{ add: { index: 'blogs', alias: 'alias1' } }] }, }); - await PageObjects.settings.createIndexPattern('alias1', false); + await PageObjects.settings.createIndexPattern('alias1', null); }); it('can delete an index pattern', async () => { diff --git a/test/functional/apps/management/_exclude_index_pattern.js b/test/functional/apps/management/_exclude_index_pattern.ts similarity index 89% rename from test/functional/apps/management/_exclude_index_pattern.js rename to test/functional/apps/management/_exclude_index_pattern.ts index b71222c1ec44d..8c20acdc21f92 100644 --- a/test/functional/apps/management/_exclude_index_pattern.js +++ b/test/functional/apps/management/_exclude_index_pattern.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['settings']); const es = getService('es'); diff --git a/test/functional/apps/management/_handle_alias.js b/test/functional/apps/management/_handle_alias.ts similarity index 95% rename from test/functional/apps/management/_handle_alias.js rename to test/functional/apps/management/_handle_alias.ts index 891e59d84a04b..04496bf9ed758 100644 --- a/test/functional/apps/management/_handle_alias.js +++ b/test/functional/apps/management/_handle_alias.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const es = getService('es'); const retry = getService('retry'); diff --git a/test/functional/apps/management/_handle_version_conflict.js b/test/functional/apps/management/_handle_version_conflict.ts similarity index 96% rename from test/functional/apps/management/_handle_version_conflict.js rename to test/functional/apps/management/_handle_version_conflict.ts index a04c5d34b2d35..2f65f966c5596 100644 --- a/test/functional/apps/management/_handle_version_conflict.js +++ b/test/functional/apps/management/_handle_version_conflict.ts @@ -16,8 +16,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); const browser = getService('browser'); @@ -93,7 +94,6 @@ export default function ({ getService, getPageObjects }) { expect(response.body.result).to.be('updated'); await PageObjects.settings.controlChangeSave(); await retry.try(async function () { - //await PageObjects.common.sleep(2000); const message = await PageObjects.common.closeToast(); expect(message).to.contain('Unable'); }); diff --git a/test/functional/apps/management/_index_pattern_filter.js b/test/functional/apps/management/_index_pattern_filter.ts similarity index 90% rename from test/functional/apps/management/_index_pattern_filter.js rename to test/functional/apps/management/_index_pattern_filter.ts index 3e9d316b59c61..afa64c474d39d 100644 --- a/test/functional/apps/management/_index_pattern_filter.js +++ b/test/functional/apps/management/_index_pattern_filter.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const retry = getService('retry'); const PageObjects = getPageObjects(['settings']); @@ -23,7 +24,7 @@ export default function ({ getService, getPageObjects }) { }); beforeEach(async function () { - await PageObjects.settings.createIndexPattern(); + await PageObjects.settings.createIndexPattern('logstash-*'); }); afterEach(async function () { diff --git a/test/functional/apps/management/_index_pattern_popularity.js b/test/functional/apps/management/_index_pattern_popularity.ts similarity index 92% rename from test/functional/apps/management/_index_pattern_popularity.js rename to test/functional/apps/management/_index_pattern_popularity.ts index 1a71e4c5fbc68..bff6cdce0f7a6 100644 --- a/test/functional/apps/management/_index_pattern_popularity.js +++ b/test/functional/apps/management/_index_pattern_popularity.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); const log = getService('log'); @@ -23,7 +24,7 @@ export default function ({ getService, getPageObjects }) { }); beforeEach(async () => { - await PageObjects.settings.createIndexPattern(); + await PageObjects.settings.createIndexPattern('logstash-*'); // increase Popularity of geo.coordinates log.debug('Starting openControlsByName (' + fieldName + ')'); await PageObjects.settings.openControlsByName(fieldName); diff --git a/test/functional/apps/management/_index_pattern_results_sort.js b/test/functional/apps/management/_index_pattern_results_sort.ts similarity index 90% rename from test/functional/apps/management/_index_pattern_results_sort.js rename to test/functional/apps/management/_index_pattern_results_sort.ts index cedf5ee355b36..305a72889e95a 100644 --- a/test/functional/apps/management/_index_pattern_results_sort.js +++ b/test/functional/apps/management/_index_pattern_results_sort.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const retry = getService('retry'); const PageObjects = getPageObjects(['settings', 'common']); @@ -18,7 +19,7 @@ export default function ({ getService, getPageObjects }) { // delete .kibana index and then wait for Kibana to re-create it await kibanaServer.uiSettings.replace({}); await PageObjects.settings.navigateTo(); - await PageObjects.settings.createIndexPattern(); + await PageObjects.settings.createIndexPattern('logstash-*'); }); after(async function () { @@ -30,7 +31,7 @@ export default function ({ getService, getPageObjects }) { heading: 'Name', first: '@message', last: 'xss.raw', - selector: async function () { + async selector() { const tableRow = await PageObjects.settings.getTableRow(0, 0); return await tableRow.getVisibleText(); }, @@ -39,7 +40,7 @@ export default function ({ getService, getPageObjects }) { heading: 'Type', first: '', last: 'text', - selector: async function () { + async selector() { const tableRow = await PageObjects.settings.getTableRow(0, 1); return await tableRow.getVisibleText(); }, @@ -49,7 +50,6 @@ export default function ({ getService, getPageObjects }) { columns.forEach(function (col) { describe('sort by heading - ' + col.heading, function indexPatternCreation() { it('should sort ascending', async function () { - console.log('col.heading', col.heading); if (col.heading !== 'Name') { await PageObjects.settings.sortBy(col.heading); } diff --git a/test/functional/apps/management/_kibana_settings.js b/test/functional/apps/management/_kibana_settings.ts similarity index 96% rename from test/functional/apps/management/_kibana_settings.js rename to test/functional/apps/management/_kibana_settings.ts index cfe4e88cda21d..d459643849fbc 100644 --- a/test/functional/apps/management/_kibana_settings.js +++ b/test/functional/apps/management/_kibana_settings.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const browser = getService('browser'); const PageObjects = getPageObjects(['settings', 'common', 'dashboard', 'timePicker', 'header']); diff --git a/test/functional/apps/management/_mgmt_import_saved_objects.js b/test/functional/apps/management/_mgmt_import_saved_objects.ts similarity index 80% rename from test/functional/apps/management/_mgmt_import_saved_objects.js rename to test/functional/apps/management/_mgmt_import_saved_objects.ts index 95b0bbb7ed03b..04a1bb5938322 100644 --- a/test/functional/apps/management/_mgmt_import_saved_objects.js +++ b/test/functional/apps/management/_mgmt_import_saved_objects.ts @@ -8,13 +8,14 @@ import expect from '@kbn/expect'; import path from 'path'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'settings', 'header', 'savedObjects']); - //in 6.4.0 bug the Saved Search conflict would be resolved and get imported but the visualization - //that referenced the saved search was not imported.( https://github.com/elastic/kibana/issues/22238) + // in 6.4.0 bug the Saved Search conflict would be resolved and get imported but the visualization + // that referenced the saved search was not imported.( https://github.com/elastic/kibana/issues/22238) describe('mgmt saved objects', function describeIndexTests() { before(async () => { @@ -41,7 +42,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.savedObjects.waitTableIsLoaded(); await PageObjects.savedObjects.searchForObject('mysaved'); - //instead of asserting on count- am asserting on the titles- which is more accurate than count. + // instead of asserting on count- am asserting on the titles- which is more accurate than count. const objects = await PageObjects.savedObjects.getRowTitles(); expect(objects.includes('mysavedsearch')).to.be(true); expect(objects.includes('mysavedviz')).to.be(true); diff --git a/test/functional/apps/management/_runtime_fields.js b/test/functional/apps/management/_runtime_fields.ts similarity index 91% rename from test/functional/apps/management/_runtime_fields.js rename to test/functional/apps/management/_runtime_fields.ts index 3a70df81b55d9..8ec9fb92c58ea 100644 --- a/test/functional/apps/management/_runtime_fields.js +++ b/test/functional/apps/management/_runtime_fields.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const log = getService('log'); const browser = getService('browser'); @@ -36,7 +37,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaIndexPatterns(); await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getFieldsTabCount()); + const startingCount = parseInt(await PageObjects.settings.getFieldsTabCount(), 10); await log.debug('add runtime field'); await PageObjects.settings.addRuntimeField( fieldName, @@ -51,7 +52,9 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.clickSaveField(); await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getFieldsTabCount())).to.be(startingCount + 1); + expect(parseInt(await PageObjects.settings.getFieldsTabCount(), 10)).to.be( + startingCount + 1 + ); }); }); diff --git a/test/functional/apps/management/_scripted_fields.js b/test/functional/apps/management/_scripted_fields.ts similarity index 96% rename from test/functional/apps/management/_scripted_fields.js rename to test/functional/apps/management/_scripted_fields.ts index 72f45e1fedb4d..c8c605ec7ed19 100644 --- a/test/functional/apps/management/_scripted_fields.js +++ b/test/functional/apps/management/_scripted_fields.ts @@ -23,8 +23,9 @@ // it will automatically insert a a closing square brace ], etc. import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const log = getService('log'); const browser = getService('browser'); @@ -77,7 +78,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaIndexPatterns(); await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount()); + const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); await PageObjects.settings.clickScriptedFieldsTab(); await log.debug('add scripted field'); const script = `1`; @@ -90,7 +91,7 @@ export default function ({ getService, getPageObjects }) { script ); await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount())).to.be( + expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( startingCount + 1 ); }); @@ -111,7 +112,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaIndexPatterns(); await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount()); + const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); await PageObjects.settings.clickScriptedFieldsTab(); await log.debug('add scripted field'); const script = `if (doc['machine.ram'].size() == 0) return -1; @@ -126,7 +127,7 @@ export default function ({ getService, getPageObjects }) { script ); await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount())).to.be( + expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( startingCount + 1 ); }); @@ -150,7 +151,7 @@ export default function ({ getService, getPageObjects }) { }); }); - //add a test to sort numeric scripted field + // add a test to sort numeric scripted field it('should sort scripted field value in Discover', async function () { await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName}`); // after the first click on the scripted field, it becomes secondary sort after time. @@ -201,7 +202,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaIndexPatterns(); await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount()); + const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); await PageObjects.settings.clickScriptedFieldsTab(); await log.debug('add scripted field'); await PageObjects.settings.addScriptedField( @@ -213,7 +214,7 @@ export default function ({ getService, getPageObjects }) { "if (doc['response.raw'].value == '200') { return 'good'} else { return 'bad'}" ); await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount())).to.be( + expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( startingCount + 1 ); }); @@ -237,7 +238,7 @@ export default function ({ getService, getPageObjects }) { }); }); - //add a test to sort string scripted field + // add a test to sort string scripted field it('should sort scripted field value in Discover', async function () { await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); // after the first click on the scripted field, it becomes secondary sort after time. @@ -287,7 +288,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaIndexPatterns(); await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount()); + const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); await PageObjects.settings.clickScriptedFieldsTab(); await log.debug('add scripted field'); await PageObjects.settings.addScriptedField( @@ -299,7 +300,7 @@ export default function ({ getService, getPageObjects }) { "doc['response.raw'].value == '200'" ); await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount())).to.be( + expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( startingCount + 1 ); }); @@ -335,8 +336,8 @@ export default function ({ getService, getPageObjects }) { await filterBar.removeAllFilters(); }); - //add a test to sort boolean - //existing bug: https://github.com/elastic/kibana/issues/75519 hence the issue is skipped. + // add a test to sort boolean + // existing bug: https://github.com/elastic/kibana/issues/75519 hence the issue is skipped. it.skip('should sort scripted field value in Discover', async function () { await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); // after the first click on the scripted field, it becomes secondary sort after time. @@ -374,7 +375,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaIndexPatterns(); await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount()); + const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); await PageObjects.settings.clickScriptedFieldsTab(); await log.debug('add scripted field'); await PageObjects.settings.addScriptedField( @@ -386,7 +387,7 @@ export default function ({ getService, getPageObjects }) { "doc['utc_time'].value.toEpochMilli() + (1000) * 60 * 60" ); await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount())).to.be( + expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( startingCount + 1 ); }); @@ -410,8 +411,8 @@ export default function ({ getService, getPageObjects }) { }); }); - //add a test to sort date scripted field - //https://github.com/elastic/kibana/issues/75711 + // add a test to sort date scripted field + // https://github.com/elastic/kibana/issues/75711 it.skip('should sort scripted field value in Discover', async function () { await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); // after the first click on the scripted field, it becomes secondary sort after time. diff --git a/test/functional/apps/management/_scripted_fields_filter.js b/test/functional/apps/management/_scripted_fields_filter.ts similarity index 95% rename from test/functional/apps/management/_scripted_fields_filter.js rename to test/functional/apps/management/_scripted_fields_filter.ts index abae9a300994d..82d1590819750 100644 --- a/test/functional/apps/management/_scripted_fields_filter.js +++ b/test/functional/apps/management/_scripted_fields_filter.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const retry = getService('retry'); const log = getService('log'); diff --git a/test/functional/apps/management/_scripted_fields_preview.js b/test/functional/apps/management/_scripted_fields_preview.ts similarity index 90% rename from test/functional/apps/management/_scripted_fields_preview.js rename to test/functional/apps/management/_scripted_fields_preview.ts index b6c941fe21d0a..380b4659c0f38 100644 --- a/test/functional/apps/management/_scripted_fields_preview.js +++ b/test/functional/apps/management/_scripted_fields_preview.ts @@ -7,13 +7,14 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const PageObjects = getPageObjects(['settings']); const SCRIPTED_FIELD_NAME = 'myScriptedField'; - const scriptResultToJson = (scriptResult) => { + const scriptResultToJson = (scriptResult: string) => { try { return JSON.parse(scriptResult); } catch (e) { @@ -26,7 +27,7 @@ export default function ({ getService, getPageObjects }) { await browser.setWindowSize(1200, 800); await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaIndexPatterns(); - await PageObjects.settings.createIndexPattern(); + await PageObjects.settings.createIndexPattern('logstash-*'); await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaIndexPatterns(); @@ -67,7 +68,7 @@ export default function ({ getService, getPageObjects }) { it('should display additional fields', async function () { const scriptResults = await PageObjects.settings.executeScriptedField( `doc['bytes'].value * 2`, - ['bytes'] + 'bytes' ); const [{ _id, bytes }] = scriptResultToJson(scriptResults); expect(_id).to.be.a('string'); diff --git a/test/functional/apps/management/_test_huge_fields.js b/test/functional/apps/management/_test_huge_fields.ts similarity index 90% rename from test/functional/apps/management/_test_huge_fields.js rename to test/functional/apps/management/_test_huge_fields.ts index 7b75683940928..abc338cb8abc8 100644 --- a/test/functional/apps/management/_test_huge_fields.js +++ b/test/functional/apps/management/_test_huge_fields.ts @@ -7,8 +7,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const security = getService('security'); const PageObjects = getPageObjects(['common', 'home', 'settings']); @@ -19,7 +20,7 @@ export default function ({ getService, getPageObjects }) { const EXPECTED_FIELD_COUNT = '10006'; before(async function () { - await security.testUser.setRoles(['kibana_admin', 'test_testhuge_reader'], false); + await security.testUser.setRoles(['kibana_admin', 'test_testhuge_reader']); await esArchiver.emptyKibanaIndex(); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/large_fields'); await PageObjects.settings.navigateTo(); diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index 9c0fc73a23675..98fdff82e13c5 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -563,7 +563,7 @@ export class SettingsPageObject extends FtrService { name: string, language: string, type: string, - format: Record, + format: Record | null, popularity: string, script: string ) { @@ -803,7 +803,7 @@ export class SettingsPageObject extends FtrService { await this.flyout.ensureClosed('scriptedFieldsHelpFlyout'); } - async executeScriptedField(script: string, additionalField: string) { + async executeScriptedField(script: string, additionalField?: string) { this.log.debug('execute Scripted Fields help'); await this.closeScriptedFieldHelp(); // ensure script help is closed so script input is not blocked await this.setScriptedFieldScript(script); @@ -814,7 +814,7 @@ export class SettingsPageObject extends FtrService { await this.testSubjects.click('runScriptButton'); await this.testSubjects.waitForDeleted('.euiLoadingSpinner'); } - let scriptResults; + let scriptResults: string = ''; await this.retry.try(async () => { scriptResults = await this.testSubjects.getVisibleText('scriptedFieldPreview'); }); From d0b64d9cafb7ae33bb53f6f5437e3394a7210f33 Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Mon, 7 Mar 2022 13:55:24 +0000 Subject: [PATCH 014/140] [ML] Add API tests for analytics jobs_exist and new_job_caps endpoints (#126914) * [ML] Add API tests for analytics jobs_exist and new_job_caps endpoints * [ML] Edits following review * [ML] Edit to api doc Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../ml/server/routes/data_frame_analytics.ts | 10 +- .../apis/ml/data_frame_analytics/index.ts | 2 + .../data_frame_analytics/jobs_exist_spaces.ts | 97 ++++++++++++++++ .../ml/data_frame_analytics/new_job_caps.ts | 104 ++++++++++++++++++ 4 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 x-pack/test/api_integration/apis/ml/data_frame_analytics/jobs_exist_spaces.ts create mode 100644 x-pack/test/api_integration/apis/ml/data_frame_analytics/new_job_caps.ts diff --git a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts index 2ab10bda36190..1fa7217e7d252 100644 --- a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts +++ b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts @@ -609,12 +609,12 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense, routeGuard }: Rout /** * @apiGroup DataFrameAnalytics * - * @api {post} /api/ml/data_frame/analytics/job_exists Check whether jobs exists in current or any space - * @apiName JobExists - * @apiDescription Checks if each of the jobs in the specified list of IDs exist. + * @api {post} /api/ml/data_frame/analytics/jobs_exist Check whether jobs exist in current or any space + * @apiName JobsExist + * @apiDescription Checks if each of the jobs in the specified list of IDs exists. * If allSpaces is true, the check will look across all spaces. * - * @apiSchema (params) analyticsIdSchema + * @apiSchema (params) jobsExistSchema */ router.post( { @@ -707,7 +707,7 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense, routeGuard }: Rout /** * @apiGroup DataFrameAnalytics * - * @api {get} api/data_frame/analytics/fields/:indexPattern Get fields for a pattern of indices used for analytics + * @api {get} /api/ml/data_frame/analytics/new_job_caps/:indexPattern Get fields for a pattern of indices used for analytics * @apiName AnalyticsNewJobCaps * @apiDescription Retrieve the index fields for analytics */ diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts index 21ff8f2cc64c1..9c9bcb318e7ec 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts @@ -22,5 +22,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./delete_spaces')); loadTestFile(require.resolve('./evaluate')); loadTestFile(require.resolve('./explain')); + loadTestFile(require.resolve('./jobs_exist_spaces')); + loadTestFile(require.resolve('./new_job_caps')); }); } diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/jobs_exist_spaces.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/jobs_exist_spaces.ts new file mode 100644 index 0000000000000..4934af379ae66 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/jobs_exist_spaces.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; +import { USER } from '../../../../functional/services/ml/security_common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + const spacesService = getService('spaces'); + const supertest = getService('supertestWithoutAuth'); + + const jobIdSpace1 = 'ihp_od_space1'; + const jobIdSpace2 = 'ihp_od_space2'; + const idSpace1 = 'space1'; + const idSpace2 = 'space2'; + + const initialModelMemoryLimit = '17mb'; + + async function runRequest( + space: string, + expectedStatusCode: number, + analyticsIds?: string[], + allSpaces?: boolean + ) { + const { body } = await supertest + .post(`/s/${space}/api/ml/data_frame/analytics/jobs_exist`) + .auth( + USER.ML_VIEWER_ALL_SPACES, + ml.securityCommon.getPasswordForUser(USER.ML_VIEWER_ALL_SPACES) + ) + .set(COMMON_REQUEST_HEADERS) + .send(allSpaces ? { analyticsIds, allSpaces } : { analyticsIds }) + .expect(expectedStatusCode); + + return body; + } + + describe('POST data_frame/analytics/jobs_exist with spaces', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); + await spacesService.create({ id: idSpace1, name: 'space_one', disabledFeatures: [] }); + await spacesService.create({ id: idSpace2, name: 'space_two', disabledFeatures: [] }); + + const jobConfigSpace1 = ml.commonConfig.getDFAIhpOutlierDetectionJobConfig(jobIdSpace1); + await ml.api.createDataFrameAnalyticsJob( + { ...jobConfigSpace1, model_memory_limit: initialModelMemoryLimit }, + idSpace1 + ); + + const jobConfigSpace2 = ml.commonConfig.getDFAIhpOutlierDetectionJobConfig(jobIdSpace2); + await ml.api.createDataFrameAnalyticsJob( + { ...jobConfigSpace2, model_memory_limit: initialModelMemoryLimit }, + idSpace2 + ); + + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + after(async () => { + await spacesService.delete(idSpace1); + await spacesService.delete(idSpace2); + await ml.api.cleanMlIndices(); + await ml.testResources.cleanMLSavedObjects(); + }); + + it('should find single job from same space', async () => { + const body = await runRequest(idSpace1, 200, [jobIdSpace1]); + expect(body).to.eql({ [jobIdSpace1]: { exists: true } }); + }); + + it('should not find single job from different space', async () => { + const body = await runRequest(idSpace2, 200, [jobIdSpace1]); + expect(body).to.eql({ [jobIdSpace1]: { exists: false } }); + }); + + it('should only find job from same space when called with a list of jobs', async () => { + const body = await runRequest(idSpace1, 200, [jobIdSpace1, jobIdSpace2]); + expect(body).to.eql({ + [jobIdSpace1]: { exists: true }, + [jobIdSpace2]: { exists: false }, + }); + }); + + it('should find single job from different space when run across all spaces', async () => { + const body = await runRequest(idSpace1, 200, [jobIdSpace2], true); + expect(body).to.eql({ [jobIdSpace2]: { exists: true } }); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/new_job_caps.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/new_job_caps.ts new file mode 100644 index 0000000000000..72ac632a8b8dd --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/new_job_caps.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; +import { USER } from '../../../../functional/services/ml/security_common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + const supertest = getService('supertestWithoutAuth'); + const testIndexPattern = 'ft_bank_marketing'; + + async function runRequest(indexPattern: string, expectedStatusCode: number, rollup?: boolean) { + let url = `/api/ml/data_frame/analytics/new_job_caps/${indexPattern}`; + if (rollup !== undefined) { + url += `?rollup=${rollup}`; + } + const { body } = await supertest + .get(url) + .auth( + USER.ML_VIEWER_ALL_SPACES, + ml.securityCommon.getPasswordForUser(USER.ML_VIEWER_ALL_SPACES) + ) + .set(COMMON_REQUEST_HEADERS) + .expect(expectedStatusCode); + + return body; + } + + describe('GET data_frame/analytics/new_job_caps', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification'); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('should return job capabilities of fields for an index that exists', async () => { + const body = await runRequest(testIndexPattern, 200); + await ml.testExecution.logTestStep( + `response should contain object for ${testIndexPattern} index pattern` + ); + expect(body).to.have.keys(testIndexPattern); + const testIndexPatternCaps = body[testIndexPattern]; + + // The data frame analytics UI does not use the aggs prop, so just perform basic checks this prop + await ml.testExecution.logTestStep( + `should contain aggs and fields props for ${testIndexPattern} index pattern` + ); + expect(testIndexPatternCaps).to.have.keys('aggs', 'fields'); + const aggs = testIndexPatternCaps.aggs; + expect(aggs).to.have.length(35); + + // The data frames analytics UI uses this endpoint to extract the names and types of fields, + // so check this info is present for some example fields + const fields = testIndexPatternCaps.fields; + expect(fields).to.have.length(24); + + await ml.testExecution.logTestStep( + `fields should contain expected name and type attributes for ${testIndexPattern} index pattern` + ); + const balanceTextField = fields.find((obj: any) => obj.id === 'balance'); + expect(balanceTextField).to.have.keys('name', 'type'); + expect(balanceTextField.name).to.eql('balance'); + expect(balanceTextField.type).to.eql('text'); + + const balanceKeywordField = fields.find((obj: any) => obj.id === 'balance.keyword'); + expect(balanceKeywordField).to.have.keys('name', 'type'); + expect(balanceKeywordField.name).to.eql('balance.keyword'); + expect(balanceKeywordField.type).to.eql('keyword'); + }); + + it('should fail to return job capabilities of fields for an index that does not exist', async () => { + await runRequest(`${testIndexPattern}_invalid`, 404); + }); + + it('should return empty job capabilities of fields for a non-rollup index with rollup parameter set to true', async () => { + const body = await runRequest(testIndexPattern, 200, true); + await ml.testExecution.logTestStep( + `response should contain object for ${testIndexPattern} index pattern` + ); + expect(body).to.have.keys(testIndexPattern); + const testIndexPatternCaps = body[testIndexPattern]; + + await ml.testExecution.logTestStep( + `should contain empty aggs and fields props for ${testIndexPattern} index pattern` + ); + expect(testIndexPatternCaps).to.have.keys('aggs', 'fields'); + const aggs = testIndexPatternCaps.aggs; + expect(aggs).to.have.length(0); + const fields = testIndexPatternCaps.fields; + expect(fields).to.have.length(0); + }); + }); +}; From 3fe1270dd710ce81842356b254d78e9277aae007 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Mon, 7 Mar 2022 14:56:46 +0100 Subject: [PATCH 015/140] [Lens][Embeddable] Make Embeddable resilient when toggling actions (#126558) * :bug: Push to the bottom embeddable creation to better handle lifecycles * Update x-pack/plugins/lens/public/embeddable/embeddable_component.tsx * Update x-pack/plugins/lens/public/embeddable/embeddable_component.tsx * Update x-pack/plugins/lens/public/embeddable/embeddable_component.tsx Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../embeddable/embeddable_component.tsx | 53 ++++++++++++------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index 482a5b931ed78..f44aef76ab83d 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -11,6 +11,7 @@ import type { Action, UiActionsStart } from 'src/plugins/ui_actions/public'; import type { Start as InspectorStartContract } from 'src/plugins/inspector/public'; import { EuiLoadingChart } from '@elastic/eui'; import { + EmbeddableFactory, EmbeddableInput, EmbeddableOutput, EmbeddablePanel, @@ -69,41 +70,48 @@ interface PluginsStartDependencies { } export function getEmbeddableComponent(core: CoreStart, plugins: PluginsStartDependencies) { + const { embeddable: embeddableStart, uiActions, inspector } = plugins; + const factory = embeddableStart.getEmbeddableFactory('lens')!; + const theme = core.theme; return (props: EmbeddableComponentProps) => { - const { embeddable: embeddableStart, uiActions, inspector } = plugins; - const factory = embeddableStart.getEmbeddableFactory('lens')!; const input = { ...props }; - const [embeddable, loading, error] = useEmbeddableFactory({ factory, input }); const hasActions = - Boolean(props.withDefaultActions) || (props.extraActions && props.extraActions?.length > 0); + Boolean(input.withDefaultActions) || (input.extraActions && input.extraActions?.length > 0); - const theme = core.theme; - - if (loading) { - return ; - } - - if (embeddable && hasActions) { + if (hasActions) { return ( } + factory={factory} uiActions={uiActions} inspector={inspector} actionPredicate={() => hasActions} input={input} theme={theme} - extraActions={props.extraActions} - withDefaultActions={props.withDefaultActions} + extraActions={input.extraActions} + withDefaultActions={input.withDefaultActions} /> ); } - - return ; + return ; }; } +function EmbeddableRootWrapper({ + factory, + input, +}: { + factory: EmbeddableFactory; + input: EmbeddableComponentProps; +}) { + const [embeddable, loading, error] = useEmbeddableFactory({ factory, input }); + if (loading) { + return ; + } + return ; +} + interface EmbeddablePanelWrapperProps { - embeddable: IEmbeddable; + factory: EmbeddableFactory; uiActions: PluginsStartDependencies['uiActions']; inspector: PluginsStartDependencies['inspector']; actionPredicate: (id: string) => boolean; @@ -114,7 +122,7 @@ interface EmbeddablePanelWrapperProps { } const EmbeddablePanelWrapper: FC = ({ - embeddable, + factory, uiActions, actionPredicate, inspector, @@ -123,10 +131,17 @@ const EmbeddablePanelWrapper: FC = ({ extraActions, withDefaultActions, }) => { + const [embeddable, loading] = useEmbeddableFactory({ factory, input }); useEffect(() => { - embeddable.updateInput(input); + if (embeddable) { + embeddable.updateInput(input); + } }, [embeddable, input]); + if (loading || !embeddable) { + return ; + } + return ( Date: Mon, 7 Mar 2022 15:58:24 +0200 Subject: [PATCH 016/140] [Cloud Posture] Update cloud security posture code owners (#127004) --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 691daa042bba9..0a0aa994fb70b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -425,8 +425,8 @@ x-pack/plugins/session_view @elastic/awp-platform # Security Asset Management /x-pack/plugins/osquery @elastic/security-asset-management -# Cloud Posture Security -/x-pack/plugins/cloud_security_posture/ @elastic/cloud-posture-security +# Cloud Security Posture +/x-pack/plugins/cloud_security_posture/ @elastic/cloud-security-posture-control-plane # Design (at the bottom for specificity of SASS files) **/*.scss @elastic/kibana-design From d5a1cdc1781bac6633e21d04b8598302dda3d158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=BCrkan=20YALAMAN?= Date: Mon, 7 Mar 2022 15:03:10 +0100 Subject: [PATCH 017/140] [App Search] Add an audit modal to show last changes on a modal. (#126486) * Add a last change column to the engine overview table * Add an audit modal to show last changes on the modal. --- .../enterprise_search/common/constants.ts | 1 + .../audit_logs_modal/audit_logs_modal.scss | 9 ++ .../audit_logs_modal.test.tsx | 52 ++++++++ .../audit_logs_modal/audit_logs_modal.tsx | 121 ++++++++++++++++++ .../audit_logs_modal_logic.test.ts | 53 ++++++++ .../audit_logs_modal_logic.ts | 32 +++++ .../components/tables/engine_link_helpers.tsx | 9 ++ .../components/tables/engines_table.tsx | 17 ++- .../components/tables/meta_engines_table.tsx | 14 ++ .../components/tables/shared_columns.tsx | 11 ++ .../components/engines/engines_overview.tsx | 2 + .../enterprise_search/server/plugin.ts | 9 ++ 12 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.scss create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal_logic.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal_logic.ts diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index 7a6203c994f4d..456a76d914f7d 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -89,3 +89,4 @@ export const READ_ONLY_MODE_HEADER = 'x-ent-search-read-only-mode'; export const ENTERPRISE_SEARCH_KIBANA_COOKIE = '_enterprise_search'; export const LOGS_SOURCE_ID = 'ent-search-logs'; +export const ENTERPRISE_SEARCH_AUDIT_LOGS_SOURCE_ID = 'ent-search-audit-logs'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.scss new file mode 100644 index 0000000000000..11a008a3cc51f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.scss @@ -0,0 +1,9 @@ +.auditLogsModal { + width: 75vw; +} + +@media (max-width: 1200px) { + .auditLogsModal { + width: 100vw; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.test.tsx new file mode 100644 index 0000000000000..f6687e431e983 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.test.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LogicMounter, setMockValues, setMockActions } from '../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiText, EuiModal } from '@elastic/eui'; + +import { EntSearchLogStream } from '../../../../../shared/log_stream'; + +import { AuditLogsModal } from './audit_logs_modal'; + +import { AuditLogsModalLogic } from './audit_logs_modal_logic'; + +describe('AuditLogsModal', () => { + const { mount } = new LogicMounter(AuditLogsModalLogic); + beforeEach(() => { + jest.clearAllMocks(); + mount({ isModalVisible: true }); + }); + + it('renders nothing by default', () => { + const wrapper = shallow(); + expect(wrapper.isEmptyRender()).toBe(true); + }); + + it('renders the modal when modal visible', () => { + const testEngineName = 'test-engine-123'; + const mockClose = jest.fn(); + setMockValues({ + isModalVisible: true, + engineName: testEngineName, + }); + setMockActions({ + hideModal: mockClose, + }); + + const wrapper = shallow(); + expect(wrapper.find(EntSearchLogStream).prop('query')).toBe( + `event.kind: event and event.action: audit and enterprisesearch.data_repository.name: ${testEngineName}` + ); + expect(wrapper.find(EuiText).children().text()).toBe('Showing events from last 24 hours'); + expect(wrapper.find(EuiModal).prop('onClose')).toBe(mockClose); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx new file mode 100644 index 0000000000000..3807234fd5c11 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues, useActions } from 'kea'; + +import { + EuiButton, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSpacer, + EuiText, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { ENTERPRISE_SEARCH_AUDIT_LOGS_SOURCE_ID } from '../../../../../../../common/constants'; +import { EntSearchLogStream } from '../../../../../shared/log_stream'; + +import { AuditLogsModalLogic } from './audit_logs_modal_logic'; + +import './audit_logs_modal.scss'; + +export const AuditLogsModal: React.FC = () => { + const auditLogsModalLogic = AuditLogsModalLogic(); + const { isModalVisible, engineName } = useValues(auditLogsModalLogic); + const { hideModal } = useActions(auditLogsModalLogic); + + const filters = [ + 'event.kind: event', + 'event.action: audit', + `enterprisesearch.data_repository.name: ${engineName}`, + ].join(' and '); + + return !isModalVisible ? null : ( + + + +

{engineName}

+
+
+ + + {i18n.translate('xpack.enterpriseSearch.appSearch.engines.auditLogsModal.eventTip', { + defaultMessage: 'Showing events from last 24 hours', + })} + + + + + + + + {i18n.translate('xpack.enterpriseSearch.appSearch.engines.auditLogsModal.closeButton', { + defaultMessage: 'Close', + })} + + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal_logic.test.ts new file mode 100644 index 0000000000000..f869dd145087d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal_logic.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LogicMounter } from '../../../../../__mocks__/kea_logic'; + +import { AuditLogsModalLogic } from './audit_logs_modal_logic'; + +describe('AuditLogsModalLogic', () => { + const { mount } = new LogicMounter(AuditLogsModalLogic); + + beforeEach(() => { + jest.clearAllMocks(); + mount(); + }); + + it('has excepted default values', () => { + expect(AuditLogsModalLogic.values).toEqual({ + isModalVisible: false, + engineName: '', + }); + }); + + describe('actions', () => { + describe('hideModal', () => { + it('hides the modal', () => { + mount({ + isModalVisible: true, + engineName: 'test_engine', + }); + + AuditLogsModalLogic.actions.hideModal(); + expect(AuditLogsModalLogic.values).toEqual({ + isModalVisible: false, + engineName: '', + }); + }); + }); + + describe('showModal', () => { + it('show the modal with correct engine name', () => { + AuditLogsModalLogic.actions.showModal('test-engine-123'); + expect(AuditLogsModalLogic.values).toEqual({ + isModalVisible: true, + engineName: 'test-engine-123', + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal_logic.ts new file mode 100644 index 0000000000000..afa70b4f3dee0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/audit_logs_modal/audit_logs_modal_logic.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea } from 'kea'; + +export const AuditLogsModalLogic = kea({ + path: ['enterprise_search', 'app_search', 'engines_overview', 'audit_logs_modal'], + actions: () => ({ + hideModal: true, + showModal: (engineName: string) => ({ engineName }), + }), + reducers: () => ({ + isModalVisible: [ + false, + { + showModal: () => true, + hideModal: () => false, + }, + ], + engineName: [ + '', + { + showModal: (_, { engineName }) => engineName, + hideModal: () => '', + }, + ], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.tsx index a3350d1ef9939..229e0def4700e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engine_link_helpers.tsx @@ -7,11 +7,14 @@ import React from 'react'; +import { EuiLink } from '@elastic/eui'; + import { KibanaLogic } from '../../../../../shared/kibana'; import { EuiLinkTo } from '../../../../../shared/react_router_helpers'; import { TelemetryLogic } from '../../../../../shared/telemetry'; import { ENGINE_PATH } from '../../../../routes'; import { generateEncodedPath } from '../../../../utils/encode_path_params'; +import { FormattedDateTime } from '../../../../utils/formatted_date_time'; const sendEngineTableLinkClickTelemetry = () => { TelemetryLogic.actions.sendAppSearchTelemetry({ @@ -34,3 +37,9 @@ export const renderEngineLink = (engineName: string) => ( {engineName} ); + +export const renderLastChangeLink = (dateString: string, onClick = () => {}) => ( + + {!dateString ? '-' : } + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx index 563e272a4a730..5e6ece1003e7f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/engines_table.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import { useValues } from 'kea'; +import { useActions, useValues } from 'kea'; import { EuiBasicTable, EuiBasicTableColumn, EuiTableFieldDataColumnType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -16,10 +16,13 @@ import { AppLogic } from '../../../../app_logic'; import { UNIVERSAL_LANGUAGE } from '../../../../constants'; import { EngineDetails } from '../../../engine/types'; -import { renderEngineLink } from './engine_link_helpers'; +import { AuditLogsModalLogic } from '../audit_logs_modal/audit_logs_modal_logic'; + +import { renderEngineLink, renderLastChangeLink } from './engine_link_helpers'; import { ACTIONS_COLUMN, CREATED_AT_COLUMN, + LAST_UPDATED_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, NAME_COLUMN, @@ -46,12 +49,22 @@ export const EnginesTable: React.FC = ({ myRole: { canManageEngines }, } = useValues(AppLogic); + const { showModal: showAuditLogModal } = useActions(AuditLogsModalLogic); + const columns: Array> = [ { ...NAME_COLUMN, render: (name: string) => renderEngineLink(name), }, CREATED_AT_COLUMN, + { + ...LAST_UPDATED_COLUMN, + render: (dateString: string, engineDetails) => { + return renderLastChangeLink(dateString, () => { + showAuditLogModal(engineDetails.name); + }); + }, + }, LANGUAGE_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx index f99dc7e15eaec..24eb8cc8a6b81 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/meta_engines_table.tsx @@ -14,6 +14,9 @@ import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; import { AppLogic } from '../../../../app_logic'; import { EngineDetails } from '../../../engine/types'; +import { AuditLogsModalLogic } from '../audit_logs_modal/audit_logs_modal_logic'; + +import { renderLastChangeLink } from './engine_link_helpers'; import { MetaEnginesTableExpandedRow } from './meta_engines_table_expanded_row'; import { MetaEnginesTableLogic } from './meta_engines_table_logic'; import { MetaEnginesTableNameColumnContent } from './meta_engines_table_name_column_content'; @@ -21,6 +24,7 @@ import { ACTIONS_COLUMN, BLANK_COLUMN, CREATED_AT_COLUMN, + LAST_UPDATED_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, NAME_COLUMN, @@ -49,6 +53,8 @@ export const MetaEnginesTable: React.FC = ({ myRole: { canManageMetaEngines }, } = useValues(AppLogic); + const { showModal: showAuditLogModal } = useActions(AuditLogsModalLogic); + const conflictingEnginesSets: ConflictingEnginesSets = useMemo( () => items.reduce((accumulator, metaEngine) => { @@ -89,6 +95,14 @@ export const MetaEnginesTable: React.FC = ({ ), }, CREATED_AT_COLUMN, + { + ...LAST_UPDATED_COLUMN, + render: (dateString: string, engineDetails) => { + return renderLastChangeLink(dateString, () => { + showAuditLogModal(engineDetails.name); + }); + }, + }, BLANK_COLUMN, DOCUMENT_COUNT_COLUMN, FIELD_COUNT_COLUMN, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx index 325760b641efd..b0ca36a777838 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/tables/shared_columns.tsx @@ -50,6 +50,17 @@ export const CREATED_AT_COLUMN: EuiTableFieldDataColumnType = { render: (dateString: string) => , }; +export const LAST_UPDATED_COLUMN: EuiTableFieldDataColumnType = { + field: 'updated_at', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.enginesOverview.table.column.lastUpdated', + { + defaultMessage: 'Last updated', + } + ), + dataType: 'string', +}; + export const DOCUMENT_COUNT_COLUMN: EuiTableFieldDataColumnType = { field: 'document_count', name: i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx index f8df9f5abfaa5..27cdff5d69812 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_overview.tsx @@ -21,6 +21,7 @@ import { DataPanel } from '../data_panel'; import { AppSearchPageTemplate } from '../layout'; import { EmptyState, EmptyMetaEnginesState } from './components'; +import { AuditLogsModal } from './components/audit_logs_modal/audit_logs_modal'; import { EnginesTable } from './components/tables/engines_table'; import { MetaEnginesTable } from './components/tables/meta_engines_table'; import { @@ -144,6 +145,7 @@ export const EnginesOverview: React.FC = () => { data-test-subj="metaEnginesLicenseCTA" /> )} + ); }; diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index ef9a0cea9da60..f393ca59a4411 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -27,6 +27,7 @@ import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN, LOGS_SOURCE_ID, + ENTERPRISE_SEARCH_AUDIT_LOGS_SOURCE_ID, } from '../common/constants'; import { registerTelemetryUsageCollector as registerASTelemetryUsageCollector } from './collectors/app_search/telemetry'; @@ -185,6 +186,14 @@ export class EnterpriseSearchPlugin implements Plugin { indexName: '.ent-search-*', }, }); + + infra.defineInternalSourceConfiguration(ENTERPRISE_SEARCH_AUDIT_LOGS_SOURCE_ID, { + name: 'Enterprise Search Audit Logs', + logIndices: { + type: 'index_name', + indexName: 'logs-enterprise_search*', + }, + }); } public start() {} From 90f0d8de0130ec0b00dd666df1f99bb3a62ef27a Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Mon, 7 Mar 2022 07:07:04 -0700 Subject: [PATCH 018/140] [Reporting] Use the logger from Core instead of a wrapper (#126740) * [Reporting] Use the logger from Core instead of a wrapper * fix redudant log context in execute fns Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/reporting/server/config/config.ts | 7 +- .../server/config/create_config.test.ts | 13 ++-- .../reporting/server/config/create_config.ts | 7 +- x-pack/plugins/reporting/server/core.ts | 9 +-- .../common/decrypt_job_headers.test.ts | 4 +- .../common/decrypt_job_headers.ts | 5 +- .../export_types/common/generate_png.ts | 8 +-- .../common/get_custom_logo.test.ts | 11 ++-- .../export_types/common/get_custom_logo.ts | 5 +- .../csv_searchsource/execute_job.test.ts | 11 ++-- .../csv_searchsource/execute_job.ts | 3 +- .../generate_csv/generate_csv.test.ts | 17 ++--- .../generate_csv/generate_csv.ts | 15 ++--- .../generate_csv/get_export_settings.test.ts | 12 ++-- .../generate_csv/get_export_settings.ts | 5 +- .../csv_searchsource_immediate/execute_job.ts | 5 +- .../png/execute_job/index.test.ts | 14 ++-- .../export_types/png/execute_job/index.ts | 4 +- .../export_types/png_v2/execute_job.test.ts | 12 +--- .../server/export_types/png_v2/execute_job.ts | 4 +- .../printable_pdf/execute_job/index.test.ts | 12 +--- .../printable_pdf/execute_job/index.ts | 4 +- .../printable_pdf/lib/generate_pdf.ts | 6 +- .../printable_pdf_v2/execute_job.test.ts | 12 +--- .../printable_pdf_v2/execute_job.ts | 4 +- .../printable_pdf_v2/lib/generate_pdf.ts | 12 ++-- .../server/lib/check_params_version.ts | 6 +- .../server/lib/content_stream.test.ts | 8 +-- .../reporting/server/lib/content_stream.ts | 15 ++--- .../server/lib/event_logger/adapter.test.ts | 8 +-- .../server/lib/event_logger/adapter.ts | 15 +++-- .../server/lib/event_logger/logger.test.ts | 4 +- .../server/lib/event_logger/logger.ts | 5 +- x-pack/plugins/reporting/server/lib/index.ts | 1 - .../reporting/server/lib/level_logger.ts | 65 ------------------- .../reporting/server/lib/store/store.test.ts | 10 +-- .../reporting/server/lib/store/store.ts | 17 ++--- .../server/lib/tasks/error_logger.test.ts | 4 +- .../server/lib/tasks/error_logger.ts | 4 +- .../server/lib/tasks/execute_report.test.ts | 9 +-- .../server/lib/tasks/execute_report.ts | 19 +++--- .../server/lib/tasks/monitor_report.test.ts | 9 +-- .../server/lib/tasks/monitor_reports.ts | 11 ++-- .../plugins/reporting/server/plugin.test.ts | 12 ++-- x-pack/plugins/reporting/server/plugin.ts | 8 +-- .../routes/deprecations/deprecations.ts | 10 +-- .../integration_tests/deprecations.test.ts | 6 +- .../server/routes/diagnostic/browser.ts | 6 +- .../server/routes/diagnostic/index.ts | 4 +- .../integration_tests/browser.test.ts | 4 +- .../integration_tests/screenshot.test.ts | 4 +- .../server/routes/diagnostic/screenshot.ts | 2 +- .../generate/csv_searchsource_immediate.ts | 12 ++-- .../generate/generate_from_jobparams.ts | 8 +-- .../generation_from_jobparams.test.ts | 4 +- .../plugins/reporting/server/routes/index.ts | 4 +- .../reporting/server/routes/lib/jobs_query.ts | 4 +- .../server/routes/lib/request_handler.test.ts | 10 +-- .../server/routes/lib/request_handler.ts | 12 ++-- .../test_helpers/create_mock_levellogger.ts | 29 --------- .../create_mock_reportingplugin.ts | 12 ++-- .../reporting/server/test_helpers/index.ts | 1 - x-pack/plugins/reporting/server/types.ts | 7 +- 63 files changed, 223 insertions(+), 367 deletions(-) delete mode 100644 x-pack/plugins/reporting/server/lib/level_logger.ts delete mode 100644 x-pack/plugins/reporting/server/test_helpers/create_mock_levellogger.ts diff --git a/x-pack/plugins/reporting/server/config/config.ts b/x-pack/plugins/reporting/server/config/config.ts index 00c57053653f7..269a66503a741 100644 --- a/x-pack/plugins/reporting/server/config/config.ts +++ b/x-pack/plugins/reporting/server/config/config.ts @@ -7,8 +7,7 @@ import { get } from 'lodash'; import { first } from 'rxjs/operators'; -import { CoreSetup, PluginInitializerContext } from 'src/core/server'; -import { LevelLogger } from '../lib'; +import type { CoreSetup, Logger, PluginInitializerContext } from 'kibana/server'; import { createConfig$ } from './create_config'; import { ReportingConfigType } from './schema'; @@ -63,13 +62,13 @@ export interface ReportingConfig extends Config { * @internal * @param {PluginInitializerContext} initContext * @param {CoreSetup} core - * @param {LevelLogger} logger + * @param {Logger} logger * @returns {Promise} */ export const buildConfig = async ( initContext: PluginInitializerContext, core: CoreSetup, - logger: LevelLogger + logger: Logger ): Promise => { const config$ = initContext.config.create(); const { http } = core; diff --git a/x-pack/plugins/reporting/server/config/create_config.test.ts b/x-pack/plugins/reporting/server/config/create_config.test.ts index fd8180bd46a05..f839d72e1a45d 100644 --- a/x-pack/plugins/reporting/server/config/create_config.test.ts +++ b/x-pack/plugins/reporting/server/config/create_config.test.ts @@ -6,11 +6,10 @@ */ import * as Rx from 'rxjs'; -import { CoreSetup, HttpServerInfo, PluginInitializerContext } from 'src/core/server'; -import { coreMock } from 'src/core/server/mocks'; -import { LevelLogger } from '../lib/level_logger'; -import { createMockConfigSchema, createMockLevelLogger } from '../test_helpers'; -import { ReportingConfigType } from './'; +import type { CoreSetup, HttpServerInfo, Logger, PluginInitializerContext } from 'kibana/server'; +import { coreMock, loggingSystemMock } from 'src/core/server/mocks'; +import { createMockConfigSchema } from '../test_helpers'; +import type { ReportingConfigType } from './'; import { createConfig$ } from './create_config'; const createMockConfig = ( @@ -20,14 +19,14 @@ const createMockConfig = ( describe('Reporting server createConfig$', () => { let mockCoreSetup: CoreSetup; let mockInitContext: PluginInitializerContext; - let mockLogger: jest.Mocked; + let mockLogger: jest.Mocked; beforeEach(() => { mockCoreSetup = coreMock.createSetup(); mockInitContext = coreMock.createPluginInitializerContext( createMockConfigSchema({ kibanaServer: {} }) ); - mockLogger = createMockLevelLogger(); + mockLogger = loggingSystemMock.createLogger(); }); afterEach(() => { diff --git a/x-pack/plugins/reporting/server/config/create_config.ts b/x-pack/plugins/reporting/server/config/create_config.ts index 2ac225ec4576a..ff8d00c30d4f8 100644 --- a/x-pack/plugins/reporting/server/config/create_config.ts +++ b/x-pack/plugins/reporting/server/config/create_config.ts @@ -7,11 +7,10 @@ import crypto from 'crypto'; import ipaddr from 'ipaddr.js'; +import type { CoreSetup, Logger } from 'kibana/server'; import { sum } from 'lodash'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { CoreSetup } from 'src/core/server'; -import { LevelLogger } from '../lib'; import { ReportingConfigType } from './schema'; /* @@ -22,9 +21,9 @@ import { ReportingConfigType } from './schema'; export function createConfig$( core: CoreSetup, config$: Observable, - parentLogger: LevelLogger + parentLogger: Logger ) { - const logger = parentLogger.clone(['config']); + const logger = parentLogger.get('config'); return config$.pipe( map((config) => { // encryption key diff --git a/x-pack/plugins/reporting/server/core.ts b/x-pack/plugins/reporting/server/core.ts index 745542c358a69..a4e4f43f90e1e 100644 --- a/x-pack/plugins/reporting/server/core.ts +++ b/x-pack/plugins/reporting/server/core.ts @@ -11,6 +11,7 @@ import { filter, first, map, switchMap, take } from 'rxjs/operators'; import type { BasePath, IClusterClient, + Logger, PackageInfo, PluginInitializerContext, SavedObjectsClientContract, @@ -32,7 +33,7 @@ import { REPORTING_REDIRECT_LOCATOR_STORE_KEY } from '../common/constants'; import { durationToNumber } from '../common/schema_utils'; import type { ReportingConfig, ReportingSetup } from './'; import { ReportingConfigType } from './config'; -import { checkLicense, getExportTypesRegistry, LevelLogger } from './lib'; +import { checkLicense, getExportTypesRegistry } from './lib'; import { reportingEventLoggerFactory } from './lib/event_logger/logger'; import type { IReport, ReportingStore } from './lib/store'; import { ExecuteReportTask, MonitorReportsTask, ReportTaskParams } from './lib/tasks'; @@ -45,7 +46,7 @@ export interface ReportingInternalSetup { security?: SecurityPluginSetup; spaces?: SpacesPluginSetup; taskManager: TaskManagerSetupContract; - logger: LevelLogger; + logger: Logger; status: StatusServiceSetup; } @@ -57,7 +58,7 @@ export interface ReportingInternalStart { data: DataPluginStart; fieldFormats: FieldFormatsStart; licensing: LicensingPluginStart; - logger: LevelLogger; + logger: Logger; screenshotting: ScreenshottingStart; security?: SecurityPluginStart; taskManager: TaskManagerStartContract; @@ -81,7 +82,7 @@ export class ReportingCore { public getContract: () => ReportingSetup; - constructor(private logger: LevelLogger, context: PluginInitializerContext) { + constructor(private logger: Logger, context: PluginInitializerContext) { this.packageInfo = context.env.packageInfo; const syncConfig = context.config.get(); this.deprecatedAllowedRoles = syncConfig.roles.enabled ? syncConfig.roles.allow : false; diff --git a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.test.ts b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.test.ts index b5258d91485f7..56a1c39e75aa4 100644 --- a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.test.ts @@ -5,11 +5,11 @@ * 2.0. */ +import { loggingSystemMock } from 'src/core/server/mocks'; import { cryptoFactory } from '../../lib'; -import { createMockLevelLogger } from '../../test_helpers'; import { decryptJobHeaders } from './'; -const logger = createMockLevelLogger(); +const logger = loggingSystemMock.createLogger(); const encryptHeaders = async (encryptionKey: string, headers: Record) => { const crypto = cryptoFactory(encryptionKey); diff --git a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts index f126d1edbfce3..3dfcfe362abd4 100644 --- a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts +++ b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts @@ -6,12 +6,13 @@ */ import { i18n } from '@kbn/i18n'; -import { cryptoFactory, LevelLogger } from '../../lib'; +import type { Logger } from 'kibana/server'; +import { cryptoFactory } from '../../lib'; export const decryptJobHeaders = async ( encryptionKey: string | undefined, headers: string, - logger: LevelLogger + logger: Logger ): Promise> => { try { if (typeof headers !== 'string') { diff --git a/x-pack/plugins/reporting/server/export_types/common/generate_png.ts b/x-pack/plugins/reporting/server/export_types/common/generate_png.ts index caa0b7fb91b3f..272d1c287178a 100644 --- a/x-pack/plugins/reporting/server/export_types/common/generate_png.ts +++ b/x-pack/plugins/reporting/server/export_types/common/generate_png.ts @@ -6,14 +6,14 @@ */ import apm from 'elastic-apm-node'; +import type { Logger } from 'kibana/server'; import * as Rx from 'rxjs'; import { finalize, map, tap } from 'rxjs/operators'; +import type { ReportingCore } from '../../'; import { LayoutTypes } from '../../../../screenshotting/common'; import { REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; import type { PngMetrics } from '../../../common/types'; -import { ReportingCore } from '../../'; -import { ScreenshotOptions } from '../../types'; -import { LevelLogger } from '../../lib'; +import type { ScreenshotOptions } from '../../types'; interface PngResult { buffer: Buffer; @@ -23,7 +23,7 @@ interface PngResult { export function generatePngObservable( reporting: ReportingCore, - logger: LevelLogger, + logger: Logger, options: ScreenshotOptions ): Rx.Observable { const apmTrans = apm.startTransaction('generate-png', REPORTING_TRANSACTION_TYPE); diff --git a/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.test.ts b/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.test.ts index f5675b50cfddd..850d0ae507e12 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.test.ts @@ -5,17 +5,14 @@ * 2.0. */ -import { ReportingCore } from '../..'; -import { - createMockConfigSchema, - createMockLevelLogger, - createMockReportingCore, -} from '../../test_helpers'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { ReportingCore } from '../../'; +import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; import { getCustomLogo } from './get_custom_logo'; let mockReportingPlugin: ReportingCore; -const logger = createMockLevelLogger(); +const logger = loggingSystemMock.createLogger(); beforeEach(async () => { mockReportingPlugin = await createMockReportingCore(createMockConfigSchema()); diff --git a/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.ts b/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.ts index fcabd34a642c8..1087315503988 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_custom_logo.ts @@ -5,16 +5,15 @@ * 2.0. */ -import type { Headers } from 'src/core/server'; +import type { Headers, Logger } from 'kibana/server'; import { ReportingCore } from '../../'; import { UI_SETTINGS_CUSTOM_PDF_LOGO } from '../../../common/constants'; -import { LevelLogger } from '../../lib'; export const getCustomLogo = async ( reporting: ReportingCore, headers: Headers, spaceId: string | undefined, - logger: LevelLogger + logger: Logger ) => { const fakeRequest = reporting.getFakeRequest({ headers }, spaceId, logger); const uiSettingsClient = await reporting.getUiSettingsClient(fakeRequest, logger); diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.test.ts index ee6d6daab88e0..5a8c4f1fd760c 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.test.ts @@ -16,18 +16,15 @@ jest.mock('./generate_csv/generate_csv', () => ({ }, })); -import { Writable } from 'stream'; import nodeCrypto from '@elastic/node-crypto'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { Writable } from 'stream'; import { ReportingCore } from '../../'; import { CancellationToken } from '../../../common/cancellation_token'; -import { - createMockConfigSchema, - createMockLevelLogger, - createMockReportingCore, -} from '../../test_helpers'; +import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; import { runTaskFnFactory } from './execute_job'; -const logger = createMockLevelLogger(); +const logger = loggingSystemMock.createLogger(); const encryptionKey = 'tetkey'; const headers = { sid: 'cooltestheaders' }; let encryptedHeaders: string; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts index 97f0aa65e3d68..8b5f0e5395827 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/execute_job.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { CSV_JOB_TYPE } from '../../../common/constants'; import { getFieldFormats } from '../../services'; import { RunTaskFn, RunTaskFnFactory } from '../../types'; import { decryptJobHeaders } from '../common'; @@ -19,7 +18,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = ( const config = reporting.getConfig(); return async function runTask(jobId, job, cancellationToken, stream) { - const logger = parentLogger.clone([CSV_JOB_TYPE, 'execute-job', jobId]); + const logger = parentLogger.get(`execute-job:${jobId}`); const encryptionKey = config.get('encryptionKey'); const headers = await decryptJobHeaders(encryptionKey, job.headers, logger); diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts index c525cb7c0def2..4755d153666e4 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts @@ -5,21 +5,22 @@ * 2.0. */ -import { Writable } from 'stream'; -import * as Rx from 'rxjs'; import { errors as esErrors } from '@elastic/elasticsearch'; +import type { IScopedClusterClient, IUiSettingsClient, SearchResponse } from 'kibana/server'; import { identity, range } from 'lodash'; -import { IScopedClusterClient, IUiSettingsClient, SearchResponse } from 'src/core/server'; +import * as Rx from 'rxjs'; import { elasticsearchServiceMock, + loggingSystemMock, savedObjectsClientMock, uiSettingsServiceMock, } from 'src/core/server/mocks'; import { ISearchStartSearchSource } from 'src/plugins/data/common'; -import { FieldFormatsRegistry } from 'src/plugins/field_formats/common'; import { searchSourceInstanceMock } from 'src/plugins/data/common/search/search_source/mocks'; import { IScopedSearchClient } from 'src/plugins/data/server'; import { dataPluginMock } from 'src/plugins/data/server/mocks'; +import { FieldFormatsRegistry } from 'src/plugins/field_formats/common'; +import { Writable } from 'stream'; import { ReportingConfig } from '../../../'; import { CancellationToken } from '../../../../common/cancellation_token'; import { @@ -28,11 +29,7 @@ import { UI_SETTINGS_DATEFORMAT_TZ, } from '../../../../common/constants'; import { UnknownError } from '../../../../common/errors'; -import { - createMockConfig, - createMockConfigSchema, - createMockLevelLogger, -} from '../../../test_helpers'; +import { createMockConfig, createMockConfigSchema } from '../../../test_helpers'; import { JobParamsCSV } from '../types'; import { CsvGenerator } from './generate_csv'; @@ -125,7 +122,7 @@ beforeEach(async () => { }); }); -const logger = createMockLevelLogger(); +const logger = loggingSystemMock.createLogger(); it('formats an empty search result to CSV content', async () => { const generateCsv = new CsvGenerator( diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts index 201484af9d7d0..c913706f58562 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { errors as esErrors } from '@elastic/elasticsearch'; -import type { IScopedClusterClient, IUiSettingsClient } from 'src/core/server'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { IScopedClusterClient, IUiSettingsClient, Logger } from 'kibana/server'; import type { IScopedSearchClient } from 'src/plugins/data/server'; import type { Datatable } from 'src/plugins/expressions/server'; import type { Writable } from 'stream'; @@ -32,16 +32,15 @@ import type { CancellationToken } from '../../../../common/cancellation_token'; import { CONTENT_TYPE_CSV } from '../../../../common/constants'; import { AuthenticationExpiredError, - UnknownError, ReportingError, + UnknownError, } from '../../../../common/errors'; import { byteSizeValueToNumber } from '../../../../common/schema_utils'; -import type { LevelLogger } from '../../../lib'; import type { TaskRunResult } from '../../../lib/tasks'; import type { JobParamsCSV } from '../types'; import { CsvExportSettings, getExportSettings } from './get_export_settings'; -import { MaxSizeStringBuilder } from './max_size_string_builder'; import { i18nTexts } from './i18n_texts'; +import { MaxSizeStringBuilder } from './max_size_string_builder'; interface Clients { es: IScopedClusterClient; @@ -65,7 +64,7 @@ export class CsvGenerator { private clients: Clients, private dependencies: Dependencies, private cancellationToken: CancellationToken, - private logger: LevelLogger, + private logger: Logger, private stream: Writable ) {} @@ -316,7 +315,7 @@ export class CsvGenerator { } if (!results) { - this.logger.warning(`Search results are undefined!`); + this.logger.warn(`Search results are undefined!`); break; } @@ -396,7 +395,7 @@ export class CsvGenerator { this.logger.debug(`Finished generating. Row count: ${this.csvRowCount}.`); if (!this.maxSizeReached && this.csvRowCount !== totalRecords) { - this.logger.warning( + this.logger.warn( `ES scroll returned fewer total hits than expected! ` + `Search result total hits: ${totalRecords}. Row count: ${this.csvRowCount}.` ); diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.test.ts index 2ae3e5e712d31..ef0f0062bf19b 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.test.ts @@ -12,18 +12,18 @@ import { UI_SETTINGS_SEARCH_INCLUDE_FROZEN, } from '../../../../common/constants'; import { IUiSettingsClient } from 'kibana/server'; -import { savedObjectsClientMock, uiSettingsServiceMock } from 'src/core/server/mocks'; import { - createMockConfig, - createMockConfigSchema, - createMockLevelLogger, -} from '../../../test_helpers'; + loggingSystemMock, + savedObjectsClientMock, + uiSettingsServiceMock, +} from 'src/core/server/mocks'; +import { createMockConfig, createMockConfigSchema } from '../../../test_helpers'; import { getExportSettings } from './get_export_settings'; describe('getExportSettings', () => { let uiSettingsClient: IUiSettingsClient; const config = createMockConfig(createMockConfigSchema({})); - const logger = createMockLevelLogger(); + const logger = loggingSystemMock.createLogger(); beforeEach(() => { uiSettingsClient = uiSettingsServiceMock diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.ts index 5b69e33624c5c..6a07e3184eb48 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/get_export_settings.ts @@ -6,7 +6,7 @@ */ import { ByteSizeValue } from '@kbn/config-schema'; -import { IUiSettingsClient } from 'kibana/server'; +import type { IUiSettingsClient, Logger } from 'kibana/server'; import { createEscapeValue } from '../../../../../../../src/plugins/data/common'; import { ReportingConfig } from '../../../'; import { @@ -16,7 +16,6 @@ import { UI_SETTINGS_DATEFORMAT_TZ, UI_SETTINGS_SEARCH_INCLUDE_FROZEN, } from '../../../../common/constants'; -import { LevelLogger } from '../../../lib'; export interface CsvExportSettings { timezone: string; @@ -37,7 +36,7 @@ export const getExportSettings = async ( client: IUiSettingsClient, config: ReportingConfig, timezone: string | undefined, - logger: LevelLogger + logger: Logger ): Promise => { let setTimezone: string; if (timezone) { diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts index 53e1f6ba3c95b..50ae2ab10f6e7 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts @@ -8,7 +8,6 @@ import { KibanaRequest } from 'src/core/server'; import { Writable } from 'stream'; import { CancellationToken } from '../../../common/cancellation_token'; -import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE } from '../../../common/constants'; import { TaskRunResult } from '../../lib/tasks'; import { getFieldFormats } from '../../services'; import { ReportingRequestHandlerContext, RunTaskFnFactory } from '../../types'; @@ -32,7 +31,7 @@ export const runTaskFnFactory: RunTaskFnFactory = function e parentLogger ) { const config = reporting.getConfig(); - const logger = parentLogger.clone([CSV_SEARCHSOURCE_IMMEDIATE_TYPE, 'execute-job']); + const logger = parentLogger.get('execute-job'); return async function runTask(_jobId, immediateJobParams, context, stream, req) { const job = { @@ -82,7 +81,7 @@ export const runTaskFnFactory: RunTaskFnFactory = function e const { warnings } = result; if (warnings) { warnings.forEach((warning) => { - logger.warning(warning); + logger.warn(warning); }); } diff --git a/x-pack/plugins/reporting/server/export_types/png/execute_job/index.test.ts b/x-pack/plugins/reporting/server/export_types/png/execute_job/index.test.ts index 9069ec63a8825..bc37978372ba6 100644 --- a/x-pack/plugins/reporting/server/export_types/png/execute_job/index.test.ts +++ b/x-pack/plugins/reporting/server/export_types/png/execute_job/index.test.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { Writable } from 'stream'; import * as Rx from 'rxjs'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { Writable } from 'stream'; import { ReportingCore } from '../../../'; import { CancellationToken } from '../../../../common/cancellation_token'; -import { cryptoFactory, LevelLogger } from '../../../lib'; +import { cryptoFactory } from '../../../lib'; import { createMockConfig, createMockConfigSchema, @@ -29,14 +30,7 @@ const cancellationToken = { on: jest.fn(), } as unknown as CancellationToken; -const mockLoggerFactory = { - get: jest.fn().mockImplementation(() => ({ - error: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - })), -}; -const getMockLogger = () => new LevelLogger(mockLoggerFactory); +const getMockLogger = () => loggingSystemMock.createLogger(); const mockEncryptionKey = 'abcabcsecuresecret'; const encryptHeaders = async (headers: Record) => { diff --git a/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts index 67d013740bedd..52023e53b80b5 100644 --- a/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts @@ -8,7 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { finalize, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; -import { PNG_JOB_TYPE, REPORTING_TRANSACTION_TYPE } from '../../../../common/constants'; +import { REPORTING_TRANSACTION_TYPE } from '../../../../common/constants'; import { TaskRunResult } from '../../../lib/tasks'; import { RunTaskFn, RunTaskFnFactory } from '../../../types'; import { decryptJobHeaders, getFullUrls, generatePngObservable } from '../../common'; @@ -24,7 +24,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = const apmGetAssets = apmTrans?.startSpan('get-assets', 'setup'); let apmGeneratePng: { end: () => void } | null | undefined; - const jobLogger = parentLogger.clone([PNG_JOB_TYPE, 'execute', jobId]); + const jobLogger = parentLogger.get(`execute:${jobId}`); const process$: Rx.Observable = Rx.of(1).pipe( mergeMap(() => decryptJobHeaders(encryptionKey, job.headers, jobLogger)), mergeMap((headers) => { diff --git a/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.test.ts b/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.test.ts index 1b1ad6878d78f..1403873e8da4b 100644 --- a/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.test.ts +++ b/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.test.ts @@ -6,11 +6,12 @@ */ import * as Rx from 'rxjs'; +import { loggingSystemMock } from 'src/core/server/mocks'; import { Writable } from 'stream'; import { ReportingCore } from '../../'; import { CancellationToken } from '../../../common/cancellation_token'; import { LocatorParams } from '../../../common/types'; -import { cryptoFactory, LevelLogger } from '../../lib'; +import { cryptoFactory } from '../../lib'; import { createMockConfig, createMockConfigSchema, @@ -30,14 +31,7 @@ const cancellationToken = { on: jest.fn(), } as unknown as CancellationToken; -const mockLoggerFactory = { - get: jest.fn().mockImplementation(() => ({ - error: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - })), -}; -const getMockLogger = () => new LevelLogger(mockLoggerFactory); +const getMockLogger = () => loggingSystemMock.createLogger(); const mockEncryptionKey = 'abcabcsecuresecret'; const encryptHeaders = async (headers: Record) => { diff --git a/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.ts b/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.ts index 51044aa324a1a..5df7a497adf6c 100644 --- a/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/png_v2/execute_job.ts @@ -8,7 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { finalize, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; -import { PNG_JOB_TYPE_V2, REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; +import { REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; import { TaskRunResult } from '../../lib/tasks'; import { RunTaskFn, RunTaskFnFactory } from '../../types'; import { decryptJobHeaders, generatePngObservable } from '../common'; @@ -25,7 +25,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = const apmGetAssets = apmTrans?.startSpan('get-assets', 'setup'); let apmGeneratePng: { end: () => void } | null | undefined; - const jobLogger = parentLogger.clone([PNG_JOB_TYPE_V2, 'execute', jobId]); + const jobLogger = parentLogger.get(`execute:${jobId}`); const process$: Rx.Observable = Rx.of(1).pipe( mergeMap(() => decryptJobHeaders(encryptionKey, job.headers, jobLogger)), mergeMap((headers) => { diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.test.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.test.ts index a8d2027f2ba12..7faa13486b5a1 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.test.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.test.ts @@ -6,10 +6,11 @@ */ import * as Rx from 'rxjs'; +import { loggingSystemMock } from 'src/core/server/mocks'; import { Writable } from 'stream'; import { ReportingCore } from '../../../'; import { CancellationToken } from '../../../../common/cancellation_token'; -import { cryptoFactory, LevelLogger } from '../../../lib'; +import { cryptoFactory } from '../../../lib'; import { createMockConfigSchema, createMockReportingCore } from '../../../test_helpers'; import { generatePdfObservable } from '../lib/generate_pdf'; import { TaskPayloadPDF } from '../types'; @@ -25,14 +26,7 @@ const cancellationToken = { on: jest.fn(), } as unknown as CancellationToken; -const mockLoggerFactory = { - get: jest.fn().mockImplementation(() => ({ - error: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - })), -}; -const getMockLogger = () => new LevelLogger(mockLoggerFactory); +const getMockLogger = () => loggingSystemMock.createLogger(); const mockEncryptionKey = 'testencryptionkey'; const encryptHeaders = async (headers: Record) => { diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts index ab3793935e1d8..9b4db48ed6697 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts @@ -8,7 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; -import { PDF_JOB_TYPE, REPORTING_TRANSACTION_TYPE } from '../../../../common/constants'; +import { REPORTING_TRANSACTION_TYPE } from '../../../../common/constants'; import { TaskRunResult } from '../../../lib/tasks'; import { RunTaskFn, RunTaskFnFactory } from '../../../types'; import { decryptJobHeaders, getFullUrls, getCustomLogo } from '../../common'; @@ -21,7 +21,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = const encryptionKey = config.get('encryptionKey'); return async function runTask(jobId, job, cancellationToken, stream) { - const jobLogger = parentLogger.clone([PDF_JOB_TYPE, 'execute-job', jobId]); + const jobLogger = parentLogger.get(`execute-job:${jobId}`); const apmTrans = apm.startTransaction('execute-job-pdf', REPORTING_TRANSACTION_TYPE); const apmGetAssets = apmTrans?.startSpan('get-assets', 'setup'); let apmGeneratePdf: { end: () => void } | null | undefined; diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts index a401f59b8f4bf..ff0ef2cf39af4 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts @@ -5,13 +5,13 @@ * 2.0. */ +import type { Logger } from 'kibana/server'; import { groupBy } from 'lodash'; import * as Rx from 'rxjs'; import { mergeMap, tap } from 'rxjs/operators'; +import { ReportingCore } from '../../../'; import { ScreenshotResult } from '../../../../../screenshotting/server'; import type { PdfMetrics } from '../../../../common/types'; -import { ReportingCore } from '../../../'; -import { LevelLogger } from '../../../lib'; import { ScreenshotOptions } from '../../../types'; import { PdfMaker } from '../../common/pdf'; import { getTracker } from './tracker'; @@ -34,7 +34,7 @@ interface PdfResult { export function generatePdfObservable( reporting: ReportingCore, - logger: LevelLogger, + logger: Logger, title: string, options: ScreenshotOptions, logo?: string diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.test.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.test.ts index 3cf7f82058563..efad71a64a81d 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.test.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.test.ts @@ -8,11 +8,12 @@ jest.mock('./lib/generate_pdf'); import * as Rx from 'rxjs'; +import { loggingSystemMock } from 'src/core/server/mocks'; import { Writable } from 'stream'; import { ReportingCore } from '../../'; import { CancellationToken } from '../../../common/cancellation_token'; import { LocatorParams } from '../../../common/types'; -import { cryptoFactory, LevelLogger } from '../../lib'; +import { cryptoFactory } from '../../lib'; import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; import { runTaskFnFactory } from './execute_job'; import { generatePdfObservable } from './lib/generate_pdf'; @@ -26,14 +27,7 @@ const cancellationToken = { on: jest.fn(), } as unknown as CancellationToken; -const mockLoggerFactory = { - get: jest.fn().mockImplementation(() => ({ - error: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - })), -}; -const getMockLogger = () => new LevelLogger(mockLoggerFactory); +const getMockLogger = () => loggingSystemMock.createLogger(); const mockEncryptionKey = 'testencryptionkey'; const encryptHeaders = async (headers: Record) => { diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.ts index 85684bca66b86..7f887707829cb 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/execute_job.ts @@ -8,7 +8,7 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; -import { PDF_JOB_TYPE_V2, REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; +import { REPORTING_TRANSACTION_TYPE } from '../../../common/constants'; import { TaskRunResult } from '../../lib/tasks'; import { RunTaskFn, RunTaskFnFactory } from '../../types'; import { decryptJobHeaders, getCustomLogo } from '../common'; @@ -21,7 +21,7 @@ export const runTaskFnFactory: RunTaskFnFactory> = const encryptionKey = config.get('encryptionKey'); return async function runTask(jobId, job, cancellationToken, stream) { - const jobLogger = parentLogger.clone([PDF_JOB_TYPE_V2, 'execute-job', jobId]); + const jobLogger = parentLogger.get(`execute-job:${jobId}`); const apmTrans = apm.startTransaction('execute-job-pdf-v2', REPORTING_TRANSACTION_TYPE); const apmGetAssets = apmTrans?.startSpan('get-assets', 'setup'); let apmGeneratePdf: { end: () => void } | null | undefined; diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/generate_pdf.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/generate_pdf.ts index ac922c07574b3..8bec3cac28f43 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/generate_pdf.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf_v2/lib/generate_pdf.ts @@ -5,14 +5,14 @@ * 2.0. */ +import type { Logger } from 'kibana/server'; import { groupBy } from 'lodash'; import * as Rx from 'rxjs'; import { mergeMap, tap } from 'rxjs/operators'; -import { ReportingCore } from '../../../'; -import { ScreenshotResult } from '../../../../../screenshotting/server'; -import { LocatorParams, PdfMetrics, UrlOrUrlLocatorTuple } from '../../../../common/types'; -import { LevelLogger } from '../../../lib'; -import { ScreenshotOptions } from '../../../types'; +import type { ReportingCore } from '../../../'; +import type { ScreenshotResult } from '../../../../../screenshotting/server'; +import type { LocatorParams, PdfMetrics, UrlOrUrlLocatorTuple } from '../../../../common/types'; +import type { ScreenshotOptions } from '../../../types'; import { PdfMaker } from '../../common/pdf'; import { getFullRedirectAppUrl } from '../../common/v2/get_full_redirect_app_url'; import type { TaskPayloadPDFV2 } from '../types'; @@ -36,7 +36,7 @@ interface PdfResult { export function generatePdfObservable( reporting: ReportingCore, - logger: LevelLogger, + logger: Logger, job: TaskPayloadPDFV2, title: string, locatorParams: LocatorParams[], diff --git a/x-pack/plugins/reporting/server/lib/check_params_version.ts b/x-pack/plugins/reporting/server/lib/check_params_version.ts index 7298384b87571..79237ba56677a 100644 --- a/x-pack/plugins/reporting/server/lib/check_params_version.ts +++ b/x-pack/plugins/reporting/server/lib/check_params_version.ts @@ -5,16 +5,16 @@ * 2.0. */ +import type { Logger } from 'kibana/server'; import { UNVERSIONED_VERSION } from '../../common/constants'; import type { BaseParams } from '../../common/types'; -import type { LevelLogger } from './'; -export function checkParamsVersion(jobParams: BaseParams, logger: LevelLogger) { +export function checkParamsVersion(jobParams: BaseParams, logger: Logger) { if (jobParams.version) { logger.debug(`Using reporting job params v${jobParams.version}`); return jobParams.version; } - logger.warning(`No version provided in report job params. Assuming ${UNVERSIONED_VERSION}`); + logger.warn(`No version provided in report job params. Assuming ${UNVERSIONED_VERSION}`); return UNVERSIONED_VERSION; } diff --git a/x-pack/plugins/reporting/server/lib/content_stream.test.ts b/x-pack/plugins/reporting/server/lib/content_stream.test.ts index 0c45ef2d5f5ce..069ac22258ad1 100644 --- a/x-pack/plugins/reporting/server/lib/content_stream.test.ts +++ b/x-pack/plugins/reporting/server/lib/content_stream.test.ts @@ -5,20 +5,20 @@ * 2.0. */ +import type { Logger } from 'kibana/server'; import { set } from 'lodash'; -import { elasticsearchServiceMock } from 'src/core/server/mocks'; -import { createMockLevelLogger } from '../test_helpers'; +import { elasticsearchServiceMock, loggingSystemMock } from 'src/core/server/mocks'; import { ContentStream } from './content_stream'; describe('ContentStream', () => { let client: ReturnType; - let logger: ReturnType; + let logger: Logger; let stream: ContentStream; let base64Stream: ContentStream; beforeEach(() => { client = elasticsearchServiceMock.createClusterClient().asInternalUser; - logger = createMockLevelLogger(); + logger = loggingSystemMock.createLogger(); stream = new ContentStream( client, logger, diff --git a/x-pack/plugins/reporting/server/lib/content_stream.ts b/x-pack/plugins/reporting/server/lib/content_stream.ts index c0b2d458b4d59..b09e446ff576c 100644 --- a/x-pack/plugins/reporting/server/lib/content_stream.ts +++ b/x-pack/plugins/reporting/server/lib/content_stream.ts @@ -5,14 +5,13 @@ * 2.0. */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { Duplex } from 'stream'; +import { ByteSizeValue } from '@kbn/config-schema'; +import type { ElasticsearchClient, Logger } from 'kibana/server'; import { defaults, get } from 'lodash'; import Puid from 'puid'; -import { ByteSizeValue } from '@kbn/config-schema'; -import type { ElasticsearchClient } from 'src/core/server'; -import { ReportingCore } from '..'; -import { ReportSource } from '../../common/types'; -import { LevelLogger } from './level_logger'; +import { Duplex } from 'stream'; +import type { ReportingCore } from '../'; +import type { ReportSource } from '../../common/types'; /** * @note The Elasticsearch `http.max_content_length` is including the whole POST body. @@ -87,7 +86,7 @@ export class ContentStream extends Duplex { constructor( private client: ElasticsearchClient, - private logger: LevelLogger, + private logger: Logger, private document: ContentStreamDocument, { encoding = 'base64' }: ContentStreamParameters = {} ) { @@ -348,7 +347,7 @@ export async function getContentStream( return new ContentStream( client, - logger.clone(['content_stream', document.id]), + logger.get('content_stream').get(document.id), document, parameters ); diff --git a/x-pack/plugins/reporting/server/lib/event_logger/adapter.test.ts b/x-pack/plugins/reporting/server/lib/event_logger/adapter.test.ts index aef569a49e357..90c546b198a08 100644 --- a/x-pack/plugins/reporting/server/lib/event_logger/adapter.test.ts +++ b/x-pack/plugins/reporting/server/lib/event_logger/adapter.test.ts @@ -6,11 +6,11 @@ */ import { LogMeta } from 'kibana/server'; -import { createMockLevelLogger } from '../../test_helpers'; +import { loggingSystemMock } from 'src/core/server/mocks'; import { EcsLogAdapter } from './adapter'; describe('EcsLogAdapter', () => { - const logger = createMockLevelLogger(); + const logger = loggingSystemMock.createLogger(); beforeAll(() => { jest .spyOn(global.Date, 'now') @@ -28,7 +28,7 @@ describe('EcsLogAdapter', () => { const event = { kibana: { reporting: { wins: 5000 } } } as object & LogMeta; // an object that extends LogMeta eventLogger.logEvent('hello world', event); - expect(logger.debug).toBeCalledWith('hello world', ['events'], { + expect(logger.debug).toBeCalledWith('hello world', { event: { duration: undefined, end: undefined, @@ -50,7 +50,7 @@ describe('EcsLogAdapter', () => { const event = { kibana: { reporting: { wins: 9000 } } } as object & LogMeta; // an object that extends LogMeta eventLogger.logEvent('hello duration', event); - expect(logger.debug).toBeCalledWith('hello duration', ['events'], { + expect(logger.debug).toBeCalledWith('hello duration', { event: { duration: 120000000000, end: '2021-04-12T16:02:00.000Z', diff --git a/x-pack/plugins/reporting/server/lib/event_logger/adapter.ts b/x-pack/plugins/reporting/server/lib/event_logger/adapter.ts index c9487a79d9e70..71116d8f334b5 100644 --- a/x-pack/plugins/reporting/server/lib/event_logger/adapter.ts +++ b/x-pack/plugins/reporting/server/lib/event_logger/adapter.ts @@ -6,23 +6,26 @@ */ import deepMerge from 'deepmerge'; -import { LogMeta } from 'src/core/server'; -import { LevelLogger } from '../level_logger'; -import { IReportingEventLogger } from './logger'; +import type { Logger, LogMeta } from 'kibana/server'; +import type { IReportingEventLogger } from './logger'; /** @internal */ export class EcsLogAdapter implements IReportingEventLogger { start?: Date; end?: Date; + private logger: Logger; + /** * This class provides a logging system to Reporting code, using a shape similar to the EventLog service. * The logging action causes ECS data with Reporting metrics sent to DEBUG logs. * - * @param {LevelLogger} logger - Reporting's wrapper of the core logger + * @param {Logger} logger - Reporting's wrapper of the core logger * @param {Partial} properties - initial ECS data with template for Reporting metrics */ - constructor(private logger: LevelLogger, private properties: Partial) {} + constructor(logger: Logger, private properties: Partial) { + this.logger = logger.get('events'); + } logEvent(message: string, properties: LogMeta) { if (this.start && !this.end) { @@ -44,7 +47,7 @@ export class EcsLogAdapter implements IReportingEventLogger { }); // sends an ECS object with Reporting metrics to the DEBUG logs - this.logger.debug(message, ['events'], deepMerge(newProperties, properties)); + this.logger.debug(message, deepMerge(newProperties, properties)); } startTiming() { diff --git a/x-pack/plugins/reporting/server/lib/event_logger/logger.test.ts b/x-pack/plugins/reporting/server/lib/event_logger/logger.test.ts index fa45a8d04176c..c58777747c3fd 100644 --- a/x-pack/plugins/reporting/server/lib/event_logger/logger.test.ts +++ b/x-pack/plugins/reporting/server/lib/event_logger/logger.test.ts @@ -5,8 +5,8 @@ * 2.0. */ +import { loggingSystemMock } from 'src/core/server/mocks'; import { ConcreteTaskInstance } from '../../../../task_manager/server'; -import { createMockLevelLogger } from '../../test_helpers'; import { BasePayload } from '../../types'; import { Report } from '../store'; import { ReportingEventLogger, reportingEventLoggerFactory } from './logger'; @@ -21,7 +21,7 @@ describe('Event Logger', () => { let factory: ReportingEventLogger; beforeEach(() => { - factory = reportingEventLoggerFactory(createMockLevelLogger()); + factory = reportingEventLoggerFactory(loggingSystemMock.createLogger()); }); it(`should construct with an internal seed object`, () => { diff --git a/x-pack/plugins/reporting/server/lib/event_logger/logger.ts b/x-pack/plugins/reporting/server/lib/event_logger/logger.ts index 6a7feea0c335d..965a55e24229a 100644 --- a/x-pack/plugins/reporting/server/lib/event_logger/logger.ts +++ b/x-pack/plugins/reporting/server/lib/event_logger/logger.ts @@ -6,8 +6,7 @@ */ import deepMerge from 'deepmerge'; -import { LogMeta } from 'src/core/server'; -import { LevelLogger } from '../'; +import type { Logger, LogMeta } from 'kibana/server'; import { PLUGIN_ID } from '../../../common/constants'; import type { TaskRunMetrics } from '../../../common/types'; import { IReport } from '../store'; @@ -46,7 +45,7 @@ export interface BaseEvent { } /** @internal */ -export function reportingEventLoggerFactory(logger: LevelLogger) { +export function reportingEventLoggerFactory(logger: Logger) { const genericLogger = new EcsLogAdapter(logger, { event: { provider: PLUGIN_ID } }); return class ReportingEventLogger { diff --git a/x-pack/plugins/reporting/server/lib/index.ts b/x-pack/plugins/reporting/server/lib/index.ts index 682f547380ba0..36d310fcd131b 100644 --- a/x-pack/plugins/reporting/server/lib/index.ts +++ b/x-pack/plugins/reporting/server/lib/index.ts @@ -10,7 +10,6 @@ export { checkParamsVersion } from './check_params_version'; export { ContentStream, getContentStream } from './content_stream'; export { cryptoFactory } from './crypto'; export { ExportTypesRegistry, getExportTypesRegistry } from './export_types_registry'; -export { LevelLogger } from './level_logger'; export { PassThroughStream } from './passthrough_stream'; export { statuses } from './statuses'; export { ReportingStore, IlmPolicyManager } from './store'; diff --git a/x-pack/plugins/reporting/server/lib/level_logger.ts b/x-pack/plugins/reporting/server/lib/level_logger.ts deleted file mode 100644 index 91cf6757dbee2..0000000000000 --- a/x-pack/plugins/reporting/server/lib/level_logger.ts +++ /dev/null @@ -1,65 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { LoggerFactory, LogMeta } from 'src/core/server'; - -const trimStr = (toTrim: string) => { - return typeof toTrim === 'string' ? toTrim.trim() : toTrim; -}; - -export interface GenericLevelLogger { - debug: (msg: string, tags: string[], meta: T) => void; - info: (msg: string) => void; - warning: (msg: string) => void; - error: (msg: Error) => void; -} - -export class LevelLogger implements GenericLevelLogger { - private _logger: LoggerFactory; - private _tags: string[]; - public warning: (msg: string, tags?: string[]) => void; - - constructor(logger: LoggerFactory, tags?: string[]) { - this._logger = logger; - this._tags = tags || []; - - /* - * This shortcut provides maintenance convenience: Reporting code has been - * using both .warn and .warning - */ - this.warning = this.warn.bind(this); - } - - private getLogger(tags: string[]) { - return this._logger.get(...this._tags, ...tags); - } - - public error(err: string | Error, tags: string[] = []) { - this.getLogger(tags).error(err); - } - - public warn(msg: string, tags: string[] = []) { - this.getLogger(tags).warn(msg); - } - - // only "debug" logging supports the LogMeta for now... - public debug(msg: string, tags: string[] = [], meta?: T) { - this.getLogger(tags).debug(msg, meta); - } - - public trace(msg: string, tags: string[] = []) { - this.getLogger(tags).trace(msg); - } - - public info(msg: string, tags: string[] = []) { - this.getLogger(tags).info(trimStr(msg)); - } - - public clone(tags: string[]) { - return new LevelLogger(this._logger, [...this._tags, ...tags]); - } -} diff --git a/x-pack/plugins/reporting/server/lib/store/store.test.ts b/x-pack/plugins/reporting/server/lib/store/store.test.ts index 3e8942be1ffa0..7ceafef261dd4 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.test.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.test.ts @@ -5,17 +5,13 @@ * 2.0. */ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { elasticsearchServiceMock } from 'src/core/server/mocks'; +import { elasticsearchServiceMock, loggingSystemMock } from 'src/core/server/mocks'; import { ReportingCore } from '../../'; -import { - createMockConfigSchema, - createMockLevelLogger, - createMockReportingCore, -} from '../../test_helpers'; +import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; import { Report, ReportDocument, ReportingStore, SavedReport } from './'; describe('ReportingStore', () => { - const mockLogger = createMockLevelLogger(); + const mockLogger = loggingSystemMock.createLogger(); let mockCore: ReportingCore; let mockEsClient: ReturnType; diff --git a/x-pack/plugins/reporting/server/lib/store/store.ts b/x-pack/plugins/reporting/server/lib/store/store.ts index 41fdd9580c996..7e920e718d51e 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.ts @@ -6,13 +6,14 @@ */ import { IndexResponse, UpdateResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { ElasticsearchClient } from 'src/core/server'; -import { LevelLogger, statuses } from '../'; -import { ReportingCore } from '../../'; +import type { ElasticsearchClient, Logger } from 'kibana/server'; +import { statuses } from '../'; +import type { ReportingCore } from '../../'; import { ILM_POLICY_NAME, REPORTING_SYSTEM_INDEX } from '../../../common/constants'; -import { JobStatus, ReportOutput, ReportSource } from '../../../common/types'; -import { ReportTaskParams } from '../tasks'; -import { IReport, Report, ReportDocument, SavedReport } from './'; +import type { JobStatus, ReportOutput, ReportSource } from '../../../common/types'; +import type { ReportTaskParams } from '../tasks'; +import type { IReport, Report, ReportDocument } from './'; +import { SavedReport } from './'; import { IlmPolicyManager } from './ilm_policy_manager'; import { indexTimestamp } from './index_timestamp'; import { mapping } from './mapping'; @@ -83,12 +84,12 @@ export class ReportingStore { private client?: ElasticsearchClient; private ilmPolicyManager?: IlmPolicyManager; - constructor(private reportingCore: ReportingCore, private logger: LevelLogger) { + constructor(private reportingCore: ReportingCore, private logger: Logger) { const config = reportingCore.getConfig(); this.indexPrefix = REPORTING_SYSTEM_INDEX; this.indexInterval = config.get('queue', 'indexInterval'); - this.logger = logger.clone(['store']); + this.logger = logger.get('store'); } private async getClient() { diff --git a/x-pack/plugins/reporting/server/lib/tasks/error_logger.test.ts b/x-pack/plugins/reporting/server/lib/tasks/error_logger.test.ts index 607c9c32538be..302088e6a6eb1 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/error_logger.test.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/error_logger.test.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { createMockLevelLogger } from '../../test_helpers'; +import { loggingSystemMock } from 'src/core/server/mocks'; import { errorLogger } from './error_logger'; -const logger = createMockLevelLogger(); +const logger = loggingSystemMock.createLogger(); describe('Execute Report Error Logger', () => { const errorLogSpy = jest.spyOn(logger, 'error'); diff --git a/x-pack/plugins/reporting/server/lib/tasks/error_logger.ts b/x-pack/plugins/reporting/server/lib/tasks/error_logger.ts index b4d4028230666..a67e3caeb2c78 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/error_logger.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/error_logger.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { LevelLogger } from '..'; +import type { Logger } from 'kibana/server'; const MAX_PARTIAL_ERROR_LENGTH = 1000; // 1000 of beginning, 1000 of end const ERROR_PARTIAL_SEPARATOR = '...'; @@ -15,7 +15,7 @@ const MAX_ERROR_LENGTH = MAX_PARTIAL_ERROR_LENGTH * 2 + ERROR_PARTIAL_SEPARATOR. * An error message string could be very long, as it sometimes includes huge * amount of base64 */ -export const errorLogger = (logger: LevelLogger, message: string, err?: Error) => { +export const errorLogger = (logger: Logger, message: string, err?: Error) => { if (err) { const errString = `${message}: ${err}`; const errLength = errString.length; diff --git a/x-pack/plugins/reporting/server/lib/tasks/execute_report.test.ts b/x-pack/plugins/reporting/server/lib/tasks/execute_report.test.ts index df662d963d0ed..b47df99b7a0fd 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/execute_report.test.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/execute_report.test.ts @@ -5,18 +5,15 @@ * 2.0. */ +import { loggingSystemMock } from 'src/core/server/mocks'; import { ReportingCore } from '../..'; import { RunContext } from '../../../../task_manager/server'; import { taskManagerMock } from '../../../../task_manager/server/mocks'; import { ReportingConfigType } from '../../config'; -import { - createMockConfigSchema, - createMockLevelLogger, - createMockReportingCore, -} from '../../test_helpers'; +import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; import { ExecuteReportTask } from './'; -const logger = createMockLevelLogger(); +const logger = loggingSystemMock.createLogger(); describe('Execute Report Task', () => { let mockReporting: ReportingCore; diff --git a/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts b/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts index 449f3b8da7671..4d4959eef00c4 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts @@ -6,20 +6,21 @@ */ import { UpdateResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { Logger } from 'kibana/server'; import moment from 'moment'; import * as Rx from 'rxjs'; import { timeout } from 'rxjs/operators'; import { finished, Writable } from 'stream'; import { promisify } from 'util'; -import { getContentStream, LevelLogger } from '../'; -import { ReportingCore } from '../../'; -import { +import { getContentStream } from '../'; +import type { ReportingCore } from '../../'; +import type { RunContext, TaskManagerStartContract, TaskRunCreatorFunction, } from '../../../../task_manager/server'; import { CancellationToken } from '../../../common/cancellation_token'; -import { ReportingError, UnknownError, QueueTimeoutError } from '../../../common/errors'; +import { QueueTimeoutError, ReportingError, UnknownError } from '../../../common/errors'; import { durationToNumber, numberToDuration } from '../../../common/schema_utils'; import type { ReportOutput } from '../../../common/types'; import type { ReportingConfigType } from '../../config'; @@ -60,7 +61,7 @@ function reportFromTask(task: ReportTaskParams) { export class ExecuteReportTask implements ReportingTask { public TYPE = REPORTING_EXECUTE_TYPE; - private logger: LevelLogger; + private logger: Logger; private taskManagerStart?: TaskManagerStartContract; private taskExecutors?: Map; private kibanaId?: string; @@ -70,9 +71,9 @@ export class ExecuteReportTask implements ReportingTask { constructor( private reporting: ReportingCore, private config: ReportingConfigType, - logger: LevelLogger + logger: Logger ) { - this.logger = logger.clone(['runTask']); + this.logger = logger.get('runTask'); } /* @@ -86,7 +87,7 @@ export class ExecuteReportTask implements ReportingTask { const exportTypesRegistry = reporting.getExportTypesRegistry(); const executors = new Map(); for (const exportType of exportTypesRegistry.getAll()) { - const exportTypeLogger = this.logger.clone([exportType.id]); + const exportTypeLogger = this.logger.get(exportType.jobType); const jobExecutor = exportType.runTaskFnFactory(reporting, exportTypeLogger); // The task will run the function with the job type as a param. // This allows us to retrieve the specific export type runFn when called to run an export @@ -476,7 +477,7 @@ export class ExecuteReportTask implements ReportingTask { return await this.getTaskManagerStart().schedule(taskInstance); } - private async rescheduleTask(task: ReportTaskParams, logger: LevelLogger) { + private async rescheduleTask(task: ReportTaskParams, logger: Logger) { logger.info(`Rescheduling task:${task.id} to retry after error.`); const oldTaskInstance: ReportingExecuteTaskInstance = { diff --git a/x-pack/plugins/reporting/server/lib/tasks/monitor_report.test.ts b/x-pack/plugins/reporting/server/lib/tasks/monitor_report.test.ts index d737c7032855b..b7e75de247535 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/monitor_report.test.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/monitor_report.test.ts @@ -5,18 +5,15 @@ * 2.0. */ +import { loggingSystemMock } from 'src/core/server/mocks'; import { ReportingCore } from '../..'; import { RunContext } from '../../../../task_manager/server'; import { taskManagerMock } from '../../../../task_manager/server/mocks'; import { ReportingConfigType } from '../../config'; -import { - createMockConfigSchema, - createMockLevelLogger, - createMockReportingCore, -} from '../../test_helpers'; +import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; import { MonitorReportsTask } from './'; -const logger = createMockLevelLogger(); +const logger = loggingSystemMock.createLogger(); describe('Execute Report Task', () => { let mockReporting: ReportingCore; diff --git a/x-pack/plugins/reporting/server/lib/tasks/monitor_reports.ts b/x-pack/plugins/reporting/server/lib/tasks/monitor_reports.ts index 4af28e3d1a698..1d406d7a5cc62 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/monitor_reports.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/monitor_reports.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { Logger } from 'kibana/server'; import moment from 'moment'; -import { LevelLogger, ReportingStore } from '../'; +import { ReportingStore } from '../'; import { ReportingCore } from '../../'; import { TaskManagerStartContract, TaskRunCreatorFunction } from '../../../../task_manager/server'; import { numberToDuration } from '../../../common/schema_utils'; @@ -38,7 +39,7 @@ import { ReportingTask, ReportingTaskStatus, REPORTING_MONITOR_TYPE, ReportTaskP export class MonitorReportsTask implements ReportingTask { public TYPE = REPORTING_MONITOR_TYPE; - private logger: LevelLogger; + private logger: Logger; private taskManagerStart?: TaskManagerStartContract; private store?: ReportingStore; private timeout: moment.Duration; @@ -46,9 +47,9 @@ export class MonitorReportsTask implements ReportingTask { constructor( private reporting: ReportingCore, private config: ReportingConfigType, - parentLogger: LevelLogger + parentLogger: Logger ) { - this.logger = parentLogger.clone([REPORTING_MONITOR_TYPE]); + this.logger = parentLogger.get(REPORTING_MONITOR_TYPE); this.timeout = numberToDuration(config.queue.timeout); } @@ -145,7 +146,7 @@ export class MonitorReportsTask implements ReportingTask { } // reschedule the task with TM - private async rescheduleTask(task: ReportTaskParams, logger: LevelLogger) { + private async rescheduleTask(task: ReportTaskParams, logger: Logger) { if (!this.taskManagerStart) { throw new Error('Reporting task runner has not been initialized!'); } diff --git a/x-pack/plugins/reporting/server/plugin.test.ts b/x-pack/plugins/reporting/server/plugin.test.ts index e179d847d9526..98f02668323b1 100644 --- a/x-pack/plugins/reporting/server/plugin.test.ts +++ b/x-pack/plugins/reporting/server/plugin.test.ts @@ -5,14 +5,12 @@ * 2.0. */ -import type { CoreSetup, CoreStart } from 'kibana/server'; -import { coreMock } from 'src/core/server/mocks'; +import type { CoreSetup, CoreStart, Logger } from 'kibana/server'; +import { coreMock, loggingSystemMock } from 'src/core/server/mocks'; import type { ReportingCore, ReportingInternalStart } from './core'; -import { LevelLogger } from './lib'; import { ReportingPlugin } from './plugin'; import { createMockConfigSchema, - createMockLevelLogger, createMockPluginSetup, createMockPluginStart, } from './test_helpers'; @@ -27,7 +25,7 @@ describe('Reporting Plugin', () => { let coreStart: CoreStart; let pluginSetup: ReportingSetupDeps; let pluginStart: ReportingInternalStart; - let logger: jest.Mocked; + let logger: jest.Mocked; let plugin: ReportingPlugin; beforeEach(async () => { @@ -38,9 +36,9 @@ describe('Reporting Plugin', () => { pluginSetup = createMockPluginSetup({}) as unknown as ReportingSetupDeps; pluginStart = await createMockPluginStart(coreStart, configSchema); - logger = createMockLevelLogger(); + logger = loggingSystemMock.createLogger(); plugin = new ReportingPlugin(initContext); - (plugin as unknown as { logger: LevelLogger }).logger = logger; + (plugin as unknown as { logger: Logger }).logger = logger; }); it('has a sync setup process', () => { diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index a0d4bfed7c7e0..37d6494f5e079 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -5,12 +5,12 @@ * 2.0. */ -import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/server'; +import type { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'kibana/server'; import { PLUGIN_ID } from '../common/constants'; import { ReportingCore } from './'; import { buildConfig, registerUiSettings, ReportingConfigType } from './config'; import { registerDeprecations } from './deprecations'; -import { LevelLogger, ReportingStore } from './lib'; +import { ReportingStore } from './lib'; import { registerRoutes } from './routes'; import { setFieldFormats } from './services'; import type { @@ -28,11 +28,11 @@ import { registerReportingUsageCollector } from './usage'; export class ReportingPlugin implements Plugin { - private logger: LevelLogger; + private logger: Logger; private reportingCore?: ReportingCore; constructor(private initContext: PluginInitializerContext) { - this.logger = new LevelLogger(initContext.logger.get()); + this.logger = initContext.logger.get(); } public setup(core: CoreSetup, plugins: ReportingSetupDeps) { diff --git a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts index 4c368337cd482..89d55ff04ab8f 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts @@ -5,16 +5,16 @@ * 2.0. */ import { errors } from '@elastic/elasticsearch'; -import { SecurityHasPrivilegesIndexPrivilegesCheck } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { RequestHandler } from 'src/core/server'; +import type { SecurityHasPrivilegesIndexPrivilegesCheck } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { Logger, RequestHandler } from 'kibana/server'; import { API_GET_ILM_POLICY_STATUS, API_MIGRATE_ILM_POLICY_URL, ILM_POLICY_NAME, } from '../../../common/constants'; -import { IlmPolicyStatusResponse } from '../../../common/types'; -import { ReportingCore } from '../../core'; -import { IlmPolicyManager, LevelLogger as Logger } from '../../lib'; +import type { IlmPolicyStatusResponse } from '../../../common/types'; +import type { ReportingCore } from '../../core'; +import { IlmPolicyManager } from '../../lib'; import { deprecations } from '../../lib/deprecations'; export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Logger) => { diff --git a/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts index 67d7d0c4a0c08..9c76aade058f0 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations/integration_tests/deprecations.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { loggingSystemMock } from 'src/core/server/mocks'; import { setupServer } from 'src/core/server/test_utils'; import supertest from 'supertest'; import { licensingMock } from '../../../../../licensing/server/mocks'; @@ -12,7 +13,6 @@ import { securityMock } from '../../../../../security/server/mocks'; import { API_GET_ILM_POLICY_STATUS } from '../../../../common/constants'; import { createMockConfigSchema, - createMockLevelLogger, createMockPluginSetup, createMockPluginStart, createMockReportingCore, @@ -54,7 +54,7 @@ describe(`GET ${API_GET_ILM_POLICY_STATUS}`, () => { it('correctly handles authz when security is unavailable', async () => { const core = await createReportingCore({}); - registerDeprecationsRoutes(core, createMockLevelLogger()); + registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); await server.start(); await supertest(httpSetup.server.listener) @@ -68,7 +68,7 @@ describe(`GET ${API_GET_ILM_POLICY_STATUS}`, () => { security.license.isEnabled.mockReturnValue(false); const core = await createReportingCore({ security }); - registerDeprecationsRoutes(core, createMockLevelLogger()); + registerDeprecationsRoutes(core, loggingSystemMock.createLogger()); await server.start(); await supertest(httpSetup.server.listener) diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts index f68df294b4118..fb95ad9e31880 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts @@ -6,11 +6,11 @@ */ import { i18n } from '@kbn/i18n'; -import { ReportingCore } from '../..'; +import type { Logger } from 'kibana/server'; +import type { ReportingCore } from '../..'; import { API_DIAGNOSE_URL } from '../../../common/constants'; -import { LevelLogger as Logger } from '../../lib'; import { authorizedUserPreRouting } from '../lib/authorized_user_pre_routing'; -import { DiagnosticResponse } from './'; +import type { DiagnosticResponse } from './'; const logsToHelpMap = { 'error while loading shared libraries': i18n.translate( diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/index.ts b/x-pack/plugins/reporting/server/routes/diagnostic/index.ts index 92404b76e0741..b5e2a8585afb3 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/index.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/index.ts @@ -5,10 +5,10 @@ * 2.0. */ +import type { Logger } from 'kibana/server'; +import type { ReportingCore } from '../../core'; import { registerDiagnoseBrowser } from './browser'; import { registerDiagnoseScreenshot } from './screenshot'; -import { LevelLogger as Logger } from '../../lib'; -import { ReportingCore } from '../../core'; export const registerDiagnosticRoutes = (reporting: ReportingCore, logger: Logger) => { registerDiagnoseBrowser(reporting, logger); diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts index 911807e63a9d5..dc8fdb7e6d0c8 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/browser.test.ts @@ -6,13 +6,13 @@ */ import * as Rx from 'rxjs'; +import { loggingSystemMock } from 'src/core/server/mocks'; import { setupServer } from 'src/core/server/test_utils'; import supertest from 'supertest'; import { ReportingCore } from '../../../'; import type { ScreenshottingStart } from '../../../../../screenshotting/server'; import { createMockConfigSchema, - createMockLevelLogger, createMockPluginSetup, createMockReportingCore, } from '../../../test_helpers'; @@ -27,7 +27,7 @@ const fontNotFoundMessage = 'Could not find the default font'; describe('POST /diagnose/browser', () => { jest.setTimeout(6000); const reportingSymbol = Symbol('reporting'); - const mockLogger = createMockLevelLogger(); + const mockLogger = loggingSystemMock.createLogger(); let server: SetupServerReturn['server']; let httpSetup: SetupServerReturn['httpSetup']; diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts index ad90679e67adb..3bc3f5bbb5e28 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/integration_tests/screenshot.test.ts @@ -5,13 +5,13 @@ * 2.0. */ +import { loggingSystemMock } from 'src/core/server/mocks'; import { setupServer } from 'src/core/server/test_utils'; import supertest from 'supertest'; import { ReportingCore } from '../../../'; import { generatePngObservable } from '../../../export_types/common'; import { createMockConfigSchema, - createMockLevelLogger, createMockPluginSetup, createMockReportingCore, } from '../../../test_helpers'; @@ -38,7 +38,7 @@ describe('POST /diagnose/screenshot', () => { }; const config = createMockConfigSchema({ queue: { timeout: 120000 } }); - const mockLogger = createMockLevelLogger(); + const mockLogger = loggingSystemMock.createLogger(); beforeEach(async () => { ({ server, httpSetup } = await setupServer(reportingSymbol)); diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts index 90b4c9d9a30c6..6819970fe753a 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts @@ -6,12 +6,12 @@ */ import { i18n } from '@kbn/i18n'; +import type { Logger } from 'kibana/server'; import { ReportingCore } from '../..'; import { APP_WRAPPER_CLASS } from '../../../../../../src/core/server'; import { API_DIAGNOSE_URL } from '../../../common/constants'; import { generatePngObservable } from '../../export_types/common'; import { getAbsoluteUrlFactory } from '../../export_types/common/get_absolute_url'; -import { LevelLogger as Logger } from '../../lib'; import { authorizedUserPreRouting } from '../lib/authorized_user_pre_routing'; import { DiagnosticResponse } from './'; diff --git a/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts b/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts index b6ada00ba55ab..19687b9d3ec9b 100644 --- a/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/generate/csv_searchsource_immediate.ts @@ -6,13 +6,13 @@ */ import { schema } from '@kbn/config-schema'; -import { KibanaRequest } from 'src/core/server'; -import { ReportingCore } from '../../'; +import type { KibanaRequest, Logger } from 'kibana/server'; +import type { ReportingCore } from '../../'; import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE } from '../../../common/constants'; import { runTaskFnFactory } from '../../export_types/csv_searchsource_immediate/execute_job'; -import { JobParamsDownloadCSV } from '../../export_types/csv_searchsource_immediate/types'; -import { LevelLogger as Logger, PassThroughStream } from '../../lib'; -import { BaseParams } from '../../types'; +import type { JobParamsDownloadCSV } from '../../export_types/csv_searchsource_immediate/types'; +import { PassThroughStream } from '../../lib'; +import type { BaseParams } from '../../types'; import { authorizedUserPreRouting } from '../lib/authorized_user_pre_routing'; import { RequestHandler } from '../lib/request_handler'; @@ -64,7 +64,7 @@ export function registerGenerateCsvFromSavedObjectImmediate( authorizedUserPreRouting( reporting, async (user, context, req: CsvFromSavedObjectRequest, res) => { - const logger = parentLogger.clone([CSV_SEARCHSOURCE_IMMEDIATE_TYPE]); + const logger = parentLogger.get(CSV_SEARCHSOURCE_IMMEDIATE_TYPE); const runTaskFn = runTaskFnFactory(reporting, logger); const requestHandler = new RequestHandler(reporting, user, context, req, res, logger); const stream = new PassThroughStream(); diff --git a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts b/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts index cfcb7d6d2b05c..c5e7bb2197d72 100644 --- a/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts +++ b/x-pack/plugins/reporting/server/routes/generate/generate_from_jobparams.ts @@ -7,16 +7,16 @@ import { schema } from '@kbn/config-schema'; import rison from 'rison-node'; -import { ReportingCore } from '../..'; +import type { Logger } from 'kibana/server'; +import type { ReportingCore } from '../..'; import { API_BASE_URL } from '../../../common/constants'; -import { LevelLogger } from '../../lib'; -import { BaseParams } from '../../types'; +import type { BaseParams } from '../../types'; import { authorizedUserPreRouting } from '../lib/authorized_user_pre_routing'; import { RequestHandler } from '../lib/request_handler'; const BASE_GENERATE = `${API_BASE_URL}/generate`; -export function registerJobGenerationRoutes(reporting: ReportingCore, logger: LevelLogger) { +export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Logger) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; diff --git a/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts b/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts index f6db9e92086eb..f0db06485cf44 100644 --- a/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts +++ b/x-pack/plugins/reporting/server/routes/generate/integration_tests/generation_from_jobparams.test.ts @@ -7,6 +7,7 @@ import rison from 'rison-node'; import { BehaviorSubject } from 'rxjs'; +import { loggingSystemMock } from 'src/core/server/mocks'; import { setupServer } from 'src/core/server/test_utils'; import supertest from 'supertest'; import { ReportingCore } from '../../../'; @@ -16,7 +17,6 @@ import { ExportTypesRegistry } from '../../../lib/export_types_registry'; import { Report } from '../../../lib/store'; import { createMockConfigSchema, - createMockLevelLogger, createMockPluginSetup, createMockPluginStart, createMockReportingCore, @@ -38,7 +38,7 @@ describe('POST /api/reporting/generate', () => { queue: { indexInterval: 'year', timeout: 10000, pollEnabled: true }, }); - const mockLogger = createMockLevelLogger(); + const mockLogger = loggingSystemMock.createLogger(); beforeEach(async () => { ({ server, httpSetup } = await setupServer(reportingSymbol)); diff --git a/x-pack/plugins/reporting/server/routes/index.ts b/x-pack/plugins/reporting/server/routes/index.ts index 49f602062b0c1..0cc0d1bdc6796 100644 --- a/x-pack/plugins/reporting/server/routes/index.ts +++ b/x-pack/plugins/reporting/server/routes/index.ts @@ -5,8 +5,8 @@ * 2.0. */ +import type { Logger } from 'kibana/server'; import { ReportingCore } from '..'; -import { LevelLogger } from '../lib'; import { registerDeprecationsRoutes } from './deprecations/deprecations'; import { registerDiagnosticRoutes } from './diagnostic'; import { @@ -15,7 +15,7 @@ import { } from './generate'; import { registerJobInfoRoutes } from './management'; -export function registerRoutes(reporting: ReportingCore, logger: LevelLogger) { +export function registerRoutes(reporting: ReportingCore, logger: Logger) { registerDeprecationsRoutes(reporting, logger); registerDiagnosticRoutes(reporting, logger); registerGenerateCsvFromSavedObjectImmediate(reporting, logger); diff --git a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts b/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts index 7f4d85ff14156..27126baad021d 100644 --- a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts +++ b/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts @@ -138,7 +138,7 @@ export function jobsQueryFactory(reportingCore: ReportingCore): JobsQueryFactory async get(user, id) { const { logger } = reportingCore.getPluginSetupDeps(); if (!id) { - logger.warning(`No ID provided for GET`); + logger.warn(`No ID provided for GET`); return; } @@ -163,7 +163,7 @@ export function jobsQueryFactory(reportingCore: ReportingCore): JobsQueryFactory const result = response?.hits?.hits?.[0]; if (!result?._source) { - logger.warning(`No hits resulted in search`); + logger.warn(`No hits resulted in search`); return; } diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts b/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts index d1c1dddb3c302..c97ec3285839d 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts +++ b/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts @@ -6,16 +6,12 @@ */ import { KibanaRequest, KibanaResponseFactory } from 'kibana/server'; -import { coreMock, httpServerMock } from 'src/core/server/mocks'; +import { coreMock, httpServerMock, loggingSystemMock } from 'src/core/server/mocks'; import { ReportingCore } from '../..'; import { JobParamsPDFDeprecated, TaskPayloadPDF } from '../../export_types/printable_pdf/types'; import { Report, ReportingStore } from '../../lib/store'; import { ReportApiJSON } from '../../lib/store/report'; -import { - createMockConfigSchema, - createMockLevelLogger, - createMockReportingCore, -} from '../../test_helpers'; +import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; import { ReportingRequestHandlerContext, ReportingSetup } from '../../types'; import { RequestHandler } from './request_handler'; @@ -43,7 +39,7 @@ const getMockResponseFactory = () => unauthorized: (obj: unknown) => obj, } as unknown as KibanaResponseFactory); -const mockLogger = createMockLevelLogger(); +const mockLogger = loggingSystemMock.createLogger(); describe('Handle request to generate', () => { let reportingCore: ReportingCore; diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts b/x-pack/plugins/reporting/server/routes/lib/request_handler.ts index b0a2032c18f19..b8a3a4c69802c 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.ts +++ b/x-pack/plugins/reporting/server/routes/lib/request_handler.ts @@ -7,12 +7,12 @@ import Boom from '@hapi/boom'; import { i18n } from '@kbn/i18n'; -import { KibanaRequest, KibanaResponseFactory } from 'kibana/server'; -import { ReportingCore } from '../..'; +import type { KibanaRequest, KibanaResponseFactory, Logger } from 'kibana/server'; +import type { ReportingCore } from '../..'; import { API_BASE_URL } from '../../../common/constants'; -import { checkParamsVersion, cryptoFactory, LevelLogger } from '../../lib'; +import { checkParamsVersion, cryptoFactory } from '../../lib'; import { Report } from '../../lib/store'; -import { BaseParams, ReportingRequestHandlerContext, ReportingUser } from '../../types'; +import type { BaseParams, ReportingRequestHandlerContext, ReportingUser } from '../../types'; export const handleUnavailable = (res: KibanaResponseFactory) => { return res.custom({ statusCode: 503, body: 'Not Available' }); @@ -30,7 +30,7 @@ export class RequestHandler { private context: ReportingRequestHandlerContext, private req: KibanaRequest, private res: KibanaResponseFactory, - private logger: LevelLogger + private logger: Logger ) {} private async encryptHeaders() { @@ -53,7 +53,7 @@ export class RequestHandler { } const [createJob, store] = await Promise.all([ - exportType.createJobFnFactory(reporting, logger.clone([exportType.id])), + exportType.createJobFnFactory(reporting, logger.get(exportType.id)), reporting.getStore(), ]); diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_levellogger.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_levellogger.ts deleted file mode 100644 index a6e6be47bdfcd..0000000000000 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_levellogger.ts +++ /dev/null @@ -1,29 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -jest.mock('../lib/level_logger'); - -import { loggingSystemMock } from 'src/core/server/mocks'; -import { LevelLogger } from '../lib/level_logger'; - -export function createMockLevelLogger() { - // eslint-disable-next-line no-console - const consoleLogger = (tag: string) => (message: unknown) => console.log(tag, message); - - const logger = new LevelLogger(loggingSystemMock.create()) as jest.Mocked; - - // logger.debug.mockImplementation(consoleLogger('debug')); // uncomment this to see debug logs in jest tests - logger.info.mockImplementation(consoleLogger('info')); - logger.warn.mockImplementation(consoleLogger('warn')); - logger.warning = jest.fn().mockImplementation(consoleLogger('warn')); - logger.error.mockImplementation(consoleLogger('error')); - logger.trace.mockImplementation(consoleLogger('trace')); - - logger.clone.mockImplementation(() => logger); - - return logger; -} diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts index 49d92a0fe4448..e00ebd99f0420 100644 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts +++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts @@ -10,7 +10,12 @@ jest.mock('../usage'); import _ from 'lodash'; import { BehaviorSubject } from 'rxjs'; -import { coreMock, elasticsearchServiceMock, statusServiceMock } from 'src/core/server/mocks'; +import { + coreMock, + elasticsearchServiceMock, + loggingSystemMock, + statusServiceMock, +} from 'src/core/server/mocks'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { dataPluginMock } from 'src/plugins/data/server/mocks'; import { FieldFormatsRegistry } from 'src/plugins/field_formats/common'; @@ -27,7 +32,6 @@ import { buildConfig, ReportingConfigType } from '../config'; import { ReportingInternalSetup, ReportingInternalStart } from '../core'; import { ReportingStore } from '../lib'; import { setFieldFormats } from '../services'; -import { createMockLevelLogger } from './create_mock_levellogger'; export const createMockPluginSetup = ( setupMock: Partial> @@ -38,13 +42,13 @@ export const createMockPluginSetup = ( router: { get: jest.fn(), post: jest.fn(), put: jest.fn(), delete: jest.fn() }, security: securityMock.createSetup(), taskManager: taskManagerMock.createSetup(), - logger: createMockLevelLogger(), + logger: loggingSystemMock.createLogger(), status: statusServiceMock.createSetupContract(), ...setupMock, }; }; -const logger = createMockLevelLogger(); +const logger = loggingSystemMock.createLogger(); const createMockReportingStore = async (config: ReportingConfigType) => { const mockConfigSchema = createMockConfigSchema(config); diff --git a/x-pack/plugins/reporting/server/test_helpers/index.ts b/x-pack/plugins/reporting/server/test_helpers/index.ts index df0a182075341..0e1dffe142c74 100644 --- a/x-pack/plugins/reporting/server/test_helpers/index.ts +++ b/x-pack/plugins/reporting/server/test_helpers/index.ts @@ -5,7 +5,6 @@ * 2.0. */ -export { createMockLevelLogger } from './create_mock_levellogger'; export { createMockConfig, createMockConfigSchema, diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts index fa69509d16be8..b3c9261bfd924 100644 --- a/x-pack/plugins/reporting/server/types.ts +++ b/x-pack/plugins/reporting/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from 'src/core/server'; +import type { IRouter, Logger, RequestHandlerContext } from 'kibana/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import type { DataPluginStart } from 'src/plugins/data/server/plugin'; import { FieldFormatsStart } from 'src/plugins/field_formats/server'; @@ -29,7 +29,6 @@ import type { CancellationToken } from '../common/cancellation_token'; import type { BaseParams, BasePayload, TaskRunResult, UrlOrUrlLocatorTuple } from '../common/types'; import type { ReportingConfigType } from './config'; import type { ReportingCore } from './core'; -import type { LevelLogger } from './lib'; import type { ReportTaskParams } from './lib/tasks'; /** @@ -71,12 +70,12 @@ export type RunTaskFn = ( export type CreateJobFnFactory = ( reporting: ReportingCore, - logger: LevelLogger + logger: Logger ) => CreateJobFnType; export type RunTaskFnFactory = ( reporting: ReportingCore, - logger: LevelLogger + logger: Logger ) => RunTaskFnType; export interface ExportTypeDefinition< From a79562a67e1dcffe18b5da7c08dde57a1374acef Mon Sep 17 00:00:00 2001 From: Sergi Massaneda Date: Mon, 7 Mar 2022 15:14:41 +0100 Subject: [PATCH 019/140] [SecuritySolution] Alerts table Fields Browser revamp (#126105) * field browser first revamp implementation * customize columns for security solution alert tables * cleaning * some tests * clean unused code * field browser tests created and existing fixed * security solution test fixes * translations cleaned * fix test * adapt cypress tests * remove translation * fix typo * remove duplicated test * type error fixed * enable body vertical scroll for small screens * fix new field not added to the table bug * addapt Kevin performance improvement * fixed linter error Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../integration/hosts/events_viewer.spec.ts | 14 +- .../timelines/fields_browser.spec.ts | 116 ++--- .../cypress/screens/fields_browser.ts | 26 +- .../cypress/tasks/fields_browser.ts | 31 +- .../components/events_viewer/index.test.tsx | 2 +- .../common/components/events_viewer/index.tsx | 12 +- .../truncatable_text/truncatable_text.tsx | 2 +- .../components/alerts_table/index.tsx | 3 +- .../detection_engine/detection_engine.tsx | 42 +- .../create_field_button/index.test.tsx | 12 +- .../create_field_button/index.tsx | 29 +- .../create_field_button/translations.ts | 0 .../components/fields_browser/field_items.tsx | 143 ------ .../fields_browser/field_name.test.tsx | 81 ---- .../components/fields_browser/field_name.tsx | 165 ------- .../field_table_columns/index.tsx | 117 +++++ .../field_table_columns/translations.ts | 36 ++ .../components/fields_browser/index.tsx | 31 ++ .../timeline/body/actions/header_actions.tsx | 4 +- .../body/column_headers/index.test.tsx | 2 +- .../timeline/body/column_headers/index.tsx | 20 +- .../components/timeline/body/index.test.tsx | 2 +- x-pack/plugins/timelines/common/index.ts | 2 +- .../search_strategy/index_fields/index.ts | 2 + .../common/types/fields_browser/index.ts | 50 +++ .../plugins/timelines/common/types/index.ts | 1 + .../common/types/timeline/actions/index.ts | 5 +- .../timelines/common/types/timeline/index.ts | 4 - .../components/fields_browser/index.tsx | 9 +- .../public/components/t_grid/body/index.tsx | 22 +- .../components/t_grid/integrated/index.tsx | 8 +- .../fields_browser/categories_badges.test.tsx | 60 +++ .../fields_browser/categories_badges.tsx | 56 +++ .../fields_browser/categories_pane.test.tsx | 51 --- .../fields_browser/categories_pane.tsx | 118 ----- .../categories_selector.test.tsx | 92 ++++ .../fields_browser/categories_selector.tsx | 173 ++++++++ .../toolbar/fields_browser/category.test.tsx | 100 ----- .../toolbar/fields_browser/category.tsx | 114 ----- .../fields_browser/category_columns.test.tsx | 153 ------- .../fields_browser/category_columns.tsx | 157 ------- .../fields_browser/category_title.test.tsx | 72 --- .../toolbar/fields_browser/category_title.tsx | 67 --- .../fields_browser/field_browser.test.tsx | 49 +-- .../toolbar/fields_browser/field_browser.tsx | 145 ++---- .../fields_browser/field_items.test.tsx | 416 +++++++----------- .../toolbar/fields_browser/field_items.tsx | 230 ++++++---- .../fields_browser/field_name.test.tsx | 2 +- .../toolbar/fields_browser/field_name.tsx | 2 +- .../fields_browser/field_table.test.tsx | 225 ++++++++++ .../toolbar/fields_browser/field_table.tsx | 126 ++++++ .../fields_browser/fields_pane.test.tsx | 112 ----- .../toolbar/fields_browser/fields_pane.tsx | 145 ------ .../toolbar/fields_browser/helpers.test.tsx | 33 -- .../t_grid/toolbar/fields_browser/helpers.tsx | 311 +------------ .../toolbar/fields_browser/index.test.tsx | 138 +++--- .../t_grid/toolbar/fields_browser/index.tsx | 108 ++--- .../toolbar/fields_browser/search.test.tsx | 74 +--- .../t_grid/toolbar/fields_browser/search.tsx | 69 +-- .../toolbar/fields_browser/translations.ts | 33 +- .../t_grid/toolbar/fields_browser/types.ts | 27 -- .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - 63 files changed, 1670 insertions(+), 2787 deletions(-) rename x-pack/plugins/security_solution/public/timelines/components/{ => fields_browser}/create_field_button/index.test.tsx (92%) rename x-pack/plugins/security_solution/public/timelines/components/{ => fields_browser}/create_field_button/index.tsx (80%) rename x-pack/plugins/security_solution/public/timelines/components/{ => fields_browser}/create_field_button/translations.ts (100%) delete mode 100644 x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_items.tsx delete mode 100644 x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/translations.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx create mode 100644 x-pack/plugins/timelines/common/types/fields_browser/index.ts create mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_badges.test.tsx create mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_badges.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_pane.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_pane.tsx create mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_selector.test.tsx create mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_selector.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_columns.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_columns.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_title.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_title.tsx create mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.test.tsx create mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.tsx delete mode 100644 x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/types.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/hosts/events_viewer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/hosts/events_viewer.spec.ts index c28c55e0eb3f7..47e71345ff0c4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/hosts/events_viewer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/hosts/events_viewer.spec.ts @@ -8,7 +8,7 @@ import { FIELDS_BROWSER_CHECKBOX, FIELDS_BROWSER_CONTAINER, - FIELDS_BROWSER_SELECTED_CATEGORY_TITLE, + FIELDS_BROWSER_SELECTED_CATEGORIES_BADGES, } from '../../screens/fields_browser'; import { HOST_GEO_CITY_NAME_HEADER, @@ -17,7 +17,11 @@ import { SERVER_SIDE_EVENT_COUNT, } from '../../screens/hosts/events'; -import { closeFieldsBrowser, filterFieldsBrowser } from '../../tasks/fields_browser'; +import { + closeFieldsBrowser, + filterFieldsBrowser, + toggleCategory, +} from '../../tasks/fields_browser'; import { loginAndWaitForPage } from '../../tasks/login'; import { openEvents } from '../../tasks/hosts/main'; import { @@ -60,11 +64,13 @@ describe('Events Viewer', () => { cy.get(FIELDS_BROWSER_CONTAINER).should('not.exist'); }); - it('displays the `default ECS` category (by default)', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', 'default ECS'); + it('displays all categories (by default)', () => { + cy.get(FIELDS_BROWSER_SELECTED_CATEGORIES_BADGES).should('be.empty'); }); it('displays a checked checkbox for all of the default events viewer columns that are also in the default ECS category', () => { + const category = 'default ECS'; + toggleCategory(category); defaultHeadersInDefaultEcsCategory.forEach((header) => cy.get(FIELDS_BROWSER_CHECKBOX(header.id)).should('be.checked') ); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/fields_browser.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/fields_browser.spec.ts index 07ea4078ce7c4..89a9fc4c0c6ba 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/fields_browser.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/fields_browser.spec.ts @@ -8,14 +8,13 @@ import { FIELDS_BROWSER_CATEGORIES_COUNT, FIELDS_BROWSER_FIELDS_COUNT, - FIELDS_BROWSER_HOST_CATEGORIES_COUNT, FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER, FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER, FIELDS_BROWSER_MESSAGE_HEADER, - FIELDS_BROWSER_SELECTED_CATEGORY_TITLE, - FIELDS_BROWSER_SELECTED_CATEGORY_COUNT, - FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT, FIELDS_BROWSER_FILTER_INPUT, + FIELDS_BROWSER_CATEGORIES_FILTER_CONTAINER, + FIELDS_BROWSER_SELECTED_CATEGORIES_BADGES, + FIELDS_BROWSER_CATEGORY_BADGE, } from '../../screens/fields_browser'; import { TIMELINE_FIELDS_BUTTON } from '../../screens/timeline'; import { cleanKibana } from '../../tasks/common'; @@ -26,13 +25,14 @@ import { clearFieldsBrowser, closeFieldsBrowser, filterFieldsBrowser, + toggleCategoryFilter, removesMessageField, resetFields, + toggleCategory, } from '../../tasks/fields_browser'; import { loginAndWaitForPage } from '../../tasks/login'; import { openTimelineUsingToggle } from '../../tasks/security_main'; import { openTimelineFieldsBrowser, populateTimeline } from '../../tasks/timeline'; -import { ecsFieldMap } from '../../../../rule_registry/common/assets/field_maps/ecs_field_map'; import { HOSTS_URL } from '../../urls/navigation'; @@ -61,21 +61,8 @@ describe('Fields Browser', () => { clearFieldsBrowser(); }); - it('displays the `default ECS` category (by default)', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', 'default ECS'); - }); - - it('the `defaultECS` (selected) category count matches the default timeline header count', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).should( - 'have.text', - `${defaultHeaders.length}` - ); - }); - - it('displays a checked checkbox for all of the default timeline columns', () => { - defaultHeaders.forEach((header) => - cy.get(`[data-test-subj="field-${header.id}-checkbox"]`).should('be.checked') - ); + it('displays all categories (by default)', () => { + cy.get(FIELDS_BROWSER_SELECTED_CATEGORIES_BADGES).should('be.empty'); }); it('displays the expected count of categories that match the filter input', () => { @@ -83,54 +70,50 @@ describe('Fields Browser', () => { filterFieldsBrowser(filterInput); - cy.get(FIELDS_BROWSER_CATEGORIES_COUNT).should('have.text', '2 categories'); + cy.get(FIELDS_BROWSER_CATEGORIES_COUNT).should('have.text', '2'); }); it('displays a search results label with the expected count of fields matching the filter input', () => { const filterInput = 'host.mac'; - filterFieldsBrowser(filterInput); - cy.get(FIELDS_BROWSER_HOST_CATEGORIES_COUNT) - .invoke('text') - .then((hostCategoriesCount) => { - cy.get(FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT) - .invoke('text') - .then((systemCategoriesCount) => { - cy.get(FIELDS_BROWSER_FIELDS_COUNT).should( - 'have.text', - `${+hostCategoriesCount + +systemCategoriesCount} fields` - ); - }); - }); - }); - - it('displays a count of only the fields in the selected category that match the filter input', () => { - const filterInput = 'host.geo.c'; + cy.get(FIELDS_BROWSER_FIELDS_COUNT).should('contain.text', '2'); + }); - filterFieldsBrowser(filterInput); + it('the `default ECS` category matches the default timeline header fields', () => { + const category = 'default ECS'; + toggleCategory(category); + cy.get(FIELDS_BROWSER_FIELDS_COUNT).should('contain.text', `${defaultHeaders.length}`); + + defaultHeaders.forEach((header) => { + cy.get(`[data-test-subj="field-${header.id}-checkbox"]`).should('be.checked'); + }); + toggleCategory(category); + }); + + it('creates the category badge when it is selected', () => { + const category = 'host'; + + cy.get(FIELDS_BROWSER_CATEGORY_BADGE(category)).should('not.exist'); + toggleCategory(category); + cy.get(FIELDS_BROWSER_CATEGORY_BADGE(category)).should('exist'); + toggleCategory(category); + }); + + it('search a category should match the category in the category filter', () => { + const category = 'host'; - const fieldsThatMatchFilterInput = Object.keys(ecsFieldMap).filter((fieldName) => { - const dotDelimitedFieldParts = fieldName.split('.'); - const fieldPartMatch = dotDelimitedFieldParts.filter((fieldPart) => { - const camelCasedStringsMatching = fieldPart - .split('_') - .some((part) => part.startsWith(filterInput)); - if (fieldPart.startsWith(filterInput)) { - return true; - } else if (camelCasedStringsMatching) { - return true; - } else { - return false; - } - }); - return fieldName.startsWith(filterInput) || fieldPartMatch.length > 0; - }).length; - - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).should( - 'have.text', - fieldsThatMatchFilterInput - ); + filterFieldsBrowser(category); + toggleCategoryFilter(); + cy.get(FIELDS_BROWSER_CATEGORIES_FILTER_CONTAINER).should('contain.text', category); + }); + + it('search a category should filter out non matching categories in the category filter', () => { + const category = 'host'; + const categoryCheck = 'event'; + filterFieldsBrowser(category); + toggleCategoryFilter(); + cy.get(FIELDS_BROWSER_CATEGORIES_FILTER_CONTAINER).should('not.contain.text', categoryCheck); }); }); @@ -157,18 +140,15 @@ describe('Fields Browser', () => { cy.get(FIELDS_BROWSER_MESSAGE_HEADER).should('not.exist'); }); - it('selects a search results label with the expected count of categories matching the filter input', () => { - const category = 'host'; - filterFieldsBrowser(category); - - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', category); - }); - it('adds a field to the timeline when the user clicks the checkbox', () => { const filterInput = 'host.geo.c'; - filterFieldsBrowser(filterInput); + closeFieldsBrowser(); cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER).should('not.exist'); + + openTimelineFieldsBrowser(); + + filterFieldsBrowser(filterInput); addsHostGeoCityNameToTimeline(); closeFieldsBrowser(); diff --git a/x-pack/plugins/security_solution/cypress/screens/fields_browser.ts b/x-pack/plugins/security_solution/cypress/screens/fields_browser.ts index 4a5f813c301db..66a7ba50c8070 100644 --- a/x-pack/plugins/security_solution/cypress/screens/fields_browser.ts +++ b/x-pack/plugins/security_solution/cypress/screens/fields_browser.ts @@ -7,20 +7,16 @@ export const CLOSE_BTN = '[data-test-subj="close"]'; -export const FIELDS_BROWSER_CATEGORIES_COUNT = '[data-test-subj="categories-count"]'; +export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]'; export const FIELDS_BROWSER_CHECKBOX = (id: string) => { - return `[data-test-subj="category-table-container"] [data-test-subj="field-${id}-checkbox"]`; + return `${FIELDS_BROWSER_CONTAINER} [data-test-subj="field-${id}-checkbox"]`; }; -export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]'; - export const FIELDS_BROWSER_FIELDS_COUNT = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="fields-count"]`; export const FIELDS_BROWSER_FILTER_INPUT = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="field-search"]`; -export const FIELDS_BROWSER_HOST_CATEGORIES_COUNT = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="host-category-count"]`; - export const FIELDS_BROWSER_HOST_GEO_CITY_NAME_CHECKBOX = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="field-host.geo.city_name-checkbox"]`; export const FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER = @@ -38,8 +34,22 @@ export const FIELDS_BROWSER_MESSAGE_HEADER = export const FIELDS_BROWSER_RESET_FIELDS = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="reset-fields"]`; -export const FIELDS_BROWSER_SELECTED_CATEGORY_COUNT = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="selected-category-count-badge"]`; +export const FIELDS_BROWSER_CATEGORIES_FILTER_BUTTON = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="categories-filter-button"]`; +export const FIELDS_BROWSER_SELECTED_CATEGORY_COUNT = `${FIELDS_BROWSER_CATEGORIES_FILTER_BUTTON} span.euiNotificationBadge`; +export const FIELDS_BROWSER_CATEGORIES_COUNT = `${FIELDS_BROWSER_CATEGORIES_FILTER_BUTTON} span.euiNotificationBadge`; -export const FIELDS_BROWSER_SELECTED_CATEGORY_TITLE = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="selected-category-title"]`; +export const FIELDS_BROWSER_SELECTED_CATEGORIES_BADGES = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="category-badges"]`; +export const FIELDS_BROWSER_CATEGORY_BADGE = (id: string) => { + return `${FIELDS_BROWSER_SELECTED_CATEGORIES_BADGES} [data-test-subj="category-badge-${id}"]`; +}; + +export const FIELDS_BROWSER_CATEGORIES_FILTER_CONTAINER = + '[data-test-subj="categories-selector-container"]'; +export const FIELDS_BROWSER_CATEGORIES_FILTER_SEARCH = + '[data-test-subj="categories-selector-search"]'; +export const FIELDS_BROWSER_CATEGORY_FILTER_OPTION = (id: string) => { + const idAttr = id.replace(/\s/g, ''); + return `${FIELDS_BROWSER_CATEGORIES_FILTER_CONTAINER} [data-test-subj="categories-selector-option-${idAttr}"]`; +}; export const FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT = `${FIELDS_BROWSER_CONTAINER} [data-test-subj="system-category-count"]`; diff --git a/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts b/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts index 941a19669f2ef..04b59305b591a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/fields_browser.ts @@ -13,6 +13,9 @@ import { FIELDS_BROWSER_RESET_FIELDS, FIELDS_BROWSER_CHECKBOX, CLOSE_BTN, + FIELDS_BROWSER_CATEGORIES_FILTER_BUTTON, + FIELDS_BROWSER_CATEGORY_FILTER_OPTION, + FIELDS_BROWSER_CATEGORIES_FILTER_SEARCH, } from '../screens/fields_browser'; export const addsFields = (fields: string[]) => { @@ -34,10 +37,9 @@ export const addsHostGeoContinentNameToTimeline = () => { }; export const clearFieldsBrowser = () => { - cy.clock(); - cy.get(FIELDS_BROWSER_FILTER_INPUT).type('{selectall}{backspace}'); - cy.wait(0); - cy.tick(1000); + cy.get(FIELDS_BROWSER_FILTER_INPUT) + .type('{selectall}{backspace}') + .waitUntil((subject) => !subject.hasClass('euiFieldSearch-isLoading')); }; export const closeFieldsBrowser = () => { @@ -46,12 +48,21 @@ export const closeFieldsBrowser = () => { }; export const filterFieldsBrowser = (fieldName: string) => { - cy.clock(); - cy.get(FIELDS_BROWSER_FILTER_INPUT).type(fieldName, { delay: 50 }); - cy.wait(0); - cy.tick(1000); - // the text filter is debounced by 250 ms, wait 1s for changes to be applied - cy.get(FIELDS_BROWSER_FILTER_INPUT).should('not.have.class', 'euiFieldSearch-isLoading'); + cy.get(FIELDS_BROWSER_FILTER_INPUT) + .clear() + .type(fieldName) + .waitUntil((subject) => !subject.hasClass('euiFieldSearch-isLoading')); +}; + +export const toggleCategoryFilter = () => { + cy.get(FIELDS_BROWSER_CATEGORIES_FILTER_BUTTON).click({ force: true }); +}; + +export const toggleCategory = (category: string) => { + toggleCategoryFilter(); + cy.get(FIELDS_BROWSER_CATEGORIES_FILTER_SEARCH).clear().type(category); + cy.get(FIELDS_BROWSER_CATEGORY_FILTER_OPTION(category)).click({ force: true }); + toggleCategoryFilter(); }; export const removesMessageField = () => { diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx index 2ecae44487908..cdc9cc9b6f32d 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx @@ -34,7 +34,7 @@ jest.mock('../../../timelines/containers', () => ({ jest.mock('../../components/url_state/normalize_time_range.ts'); const mockUseCreateFieldButton = jest.fn().mockReturnValue(<>); -jest.mock('../../../timelines/components/create_field_button', () => ({ +jest.mock('../../../timelines/components/fields_browser/create_field_button', () => ({ useCreateFieldButton: (...params: unknown[]) => mockUseCreateFieldButton(...params), })); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index 5e3fc4e81f9dc..68c4af5ee2fe8 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -30,9 +30,9 @@ import { FIELDS_WITHOUT_CELL_ACTIONS } from '../../lib/cell_actions/constants'; import { useGetUserCasesPermissions, useKibana } from '../../lib/kibana'; import { GraphOverlay } from '../../../timelines/components/graph_overlay'; import { + useFieldBrowserOptions, CreateFieldEditorActions, - useCreateFieldButton, -} from '../../../timelines/components/create_field_button'; +} from '../../../timelines/components/fields_browser'; const EMPTY_CONTROL_COLUMNS: ControlColumnProps[] = []; @@ -177,7 +177,11 @@ const StatefulEventsViewerComponent: React.FC = ({ }, [id, timelineQuery, globalQuery]); const bulkActions = useMemo(() => ({ onAlertStatusActionSuccess }), [onAlertStatusActionSuccess]); - const createFieldComponent = useCreateFieldButton(scopeId, id, editorActionsRef); + const fieldBrowserOptions = useFieldBrowserOptions({ + sourcererScope: scopeId, + timelineId: id, + editorActionsRef, + }); const casesPermissions = useGetUserCasesPermissions(); const CasesContext = casesUi.getCasesContext(); @@ -201,6 +205,7 @@ const StatefulEventsViewerComponent: React.FC = ({ docValueFields, end, entityType, + fieldBrowserOptions, filters: globalFilters, filterStatus: currentFilter, globalFullScreen, @@ -228,7 +233,6 @@ const StatefulEventsViewerComponent: React.FC = ({ trailingControlColumns, type: 'embedded', unit, - createFieldComponent, })} diff --git a/x-pack/plugins/security_solution/public/common/components/truncatable_text/truncatable_text.tsx b/x-pack/plugins/security_solution/public/common/components/truncatable_text/truncatable_text.tsx index cc1c53d107100..27369dadb8a3b 100644 --- a/x-pack/plugins/security_solution/public/common/components/truncatable_text/truncatable_text.tsx +++ b/x-pack/plugins/security_solution/public/common/components/truncatable_text/truncatable_text.tsx @@ -16,7 +16,7 @@ import { EuiToolTip } from '@elastic/eui'; * Note: Requires a parent container with a defined width or max-width. */ -const EllipsisText = styled.span` +export const EllipsisText = styled.span` &, & * { display: inline-block; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 1499e803fdf37..0f6d2d260ae0d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -99,7 +99,6 @@ export const AlertsTableComponent: React.FC = ({ const { browserFields, indexPattern: indexPatterns, - loading: indexPatternsLoading, selectedPatterns, } = useSourcererDataView(SourcererScopeName.detections); const kibana = useKibana(); @@ -360,7 +359,7 @@ export const AlertsTableComponent: React.FC = ({ const casesPermissions = useGetUserCasesPermissions(); const CasesContext = kibana.services.cases.getCasesContext(); - if (loading || indexPatternsLoading || isEmpty(selectedPatterns)) { + if (loading || isEmpty(selectedPatterns)) { return null; } diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index e4f51b05ad6d9..eccb2e081cd9d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -140,7 +140,7 @@ const DetectionEnginePageComponent: React.FC = ({ const { formatUrl } = useFormatUrl(SecurityPageName.rules); const [showBuildingBlockAlerts, setShowBuildingBlockAlerts] = useState(false); const [showOnlyThreatIndicatorAlerts, setShowOnlyThreatIndicatorAlerts] = useState(false); - const loading = userInfoLoading || listsConfigLoading || isLoadingIndexPattern; + const loading = userInfoLoading || listsConfigLoading; const { application: { navigateToUrl }, timelines: timelinesUi, @@ -341,24 +341,32 @@ const DetectionEnginePageComponent: React.FC = ({ - + {isLoadingIndexPattern ? ( + + ) : ( + + )} - + {isLoadingIndexPattern ? ( + + ) : ( + + )} diff --git a/x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.test.tsx similarity index 92% rename from x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.test.tsx rename to x-pack/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.test.tsx index 0afb2bf641351..1bddd96c05727 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.test.tsx @@ -11,15 +11,15 @@ import { CreateFieldButton, CreateFieldEditorActions } from './index'; import { indexPatternFieldEditorPluginMock, Start, -} from '../../../../../../../src/plugins/data_view_field_editor/public/mocks'; +} from '../../../../../../../../src/plugins/data_view_field_editor/public/mocks'; -import { TestProviders } from '../../../common/mock'; -import { useKibana } from '../../../common/lib/kibana'; -import type { DataView } from '../../../../../../../src/plugins/data/common'; -import { TimelineId } from '../../../../common/types'; +import { TestProviders } from '../../../../common/mock'; +import { useKibana } from '../../../../common/lib/kibana'; +import type { DataView } from '../../../../../../../../src/plugins/data/common'; +import { TimelineId } from '../../../../../common/types'; let mockIndexPatternFieldEditor: Start; -jest.mock('../../../common/lib/kibana'); +jest.mock('../../../../common/lib/kibana'); const useKibanaMock = useKibana as jest.Mocked; const runAllPromises = () => new Promise(setImmediate); diff --git a/x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.tsx similarity index 80% rename from x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.tsx rename to x-pack/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.tsx index 8979a78d7aa46..645e1f0b29aed 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/create_field_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/create_field_button/index.tsx @@ -10,23 +10,26 @@ import { EuiButton } from '@elastic/eui'; import styled from 'styled-components'; import { useDispatch } from 'react-redux'; -import type { DataViewField, DataView } from '../../../../../../../src/plugins/data_views/common'; -import { useKibana } from '../../../common/lib/kibana'; +import type { + DataViewField, + DataView, +} from '../../../../../../../../src/plugins/data_views/common'; +import { useKibana } from '../../../../common/lib/kibana'; import * as i18n from './translations'; -import { CreateFieldComponentType, TimelineId } from '../../../../../timelines/common'; -import { upsertColumn } from '../../../../../timelines/public'; -import { useDataView } from '../../../common/containers/source/use_data_view'; -import { SourcererScopeName } from '../../../common/store/sourcerer/model'; -import { sourcererSelectors } from '../../../common/store'; -import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; -import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants'; -import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers'; +import { FieldBrowserOptions, TimelineId } from '../../../../../../timelines/common'; +import { upsertColumn } from '../../../../../../timelines/public'; +import { useDataView } from '../../../../common/containers/source/use_data_view'; +import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +import { sourcererSelectors } from '../../../../common/store'; +import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; +import { DEFAULT_COLUMN_MIN_WIDTH } from '../../timeline/body/constants'; +import { defaultColumnHeaderType } from '../../timeline/body/column_headers/default_headers'; export type CreateFieldEditorActions = { closeEditor: () => void } | null; -type CreateFieldEditorActionsRef = MutableRefObject; +export type CreateFieldEditorActionsRef = MutableRefObject; -interface CreateFieldButtonProps { +export interface CreateFieldButtonProps { selectedDataViewId: string; onClick: () => void; timelineId: TimelineId; @@ -142,7 +145,7 @@ export const useCreateFieldButton = ( return; } // It receives onClick props from field browser in order to close the modal. - const CreateFieldButtonComponent: CreateFieldComponentType = ({ onClick }) => ( + const CreateFieldButtonComponent: FieldBrowserOptions['createFieldButton'] = ({ onClick }) => ( void; -}) => { - const keyboardHandlerRef = useRef(null); - const [closePopOverTrigger, setClosePopOverTrigger] = useState(false); - const [hoverActionsOwnFocus, setHoverActionsOwnFocus] = useState(false); - const { timelines } = useKibana().services; - - const handleClosePopOverTrigger = useCallback(() => { - setClosePopOverTrigger((prevClosePopOverTrigger) => !prevClosePopOverTrigger); - - setHoverActionsOwnFocus((prevHoverActionsOwnFocus) => { - if (prevHoverActionsOwnFocus) { - // on the next tick, re-focus the keyboard handler if the hover actions owned focus - setTimeout(() => { - keyboardHandlerRef.current?.focus(); - }, 0); - } - return false; // always give up ownership - }); - - setTimeout(() => { - setHoverActionsOwnFocus(false); - }, 0); // invoked on the next tick, because we want to restore focus first - }, []); - - const openPopover = useCallback(() => { - setHoverActionsOwnFocus(true); - }, [setHoverActionsOwnFocus]); - - const { onBlur, onKeyDown } = timelines.getUseDraggableKeyboardWrapper()({ - closePopover: handleClosePopOverTrigger, - draggableId: getDraggableFieldId({ - contextId: `field-browser-field-items-field-draggable-${timelineId}-${categoryId}-${fieldName}`, - fieldId: fieldName, - }), - fieldName, - keyboardHandlerRef, - openPopover, - }); - - const onFocus = useCallback(() => { - keyboardHandlerRef.current?.focus(); - }, []); - - const onCloseRequested = useCallback(() => { - setHoverActionsOwnFocus((prevHoverActionOwnFocus) => - prevHoverActionOwnFocus ? false : prevHoverActionOwnFocus - ); - - setTimeout(() => { - onFocus(); // return focus to this draggable on the next tick, because we owned focus - }, 0); - }, [onFocus]); - - return ( -
- - {(provided) => ( -
- -
- )} -
-
- ); -}; - -export const DraggableFieldsBrowserField = React.memo(DraggableFieldsBrowserFieldComponent); -DraggableFieldsBrowserField.displayName = 'DraggableFieldsBrowserFieldComponent'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx deleted file mode 100644 index 5acc0ef9aa46b..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.test.tsx +++ /dev/null @@ -1,81 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { mount } from 'enzyme'; -import React from 'react'; -import { waitFor } from '@testing-library/react'; -import { mockBrowserFields } from '../../../common/containers/source/mock'; -import { TestProviders } from '../../../common/mock'; -import '../../../common/mock/match_media'; -import { getColumnsWithTimestamp } from '../../../common/components/event_details/helpers'; - -import { FieldName } from './field_name'; - -jest.mock('../../../common/lib/kibana'); - -const categoryId = 'base'; -const timestampFieldId = '@timestamp'; - -const defaultProps = { - categoryId, - categoryColumns: getColumnsWithTimestamp({ - browserFields: mockBrowserFields, - category: categoryId, - }), - closePopOverTrigger: false, - fieldId: timestampFieldId, - handleClosePopOverTrigger: jest.fn(), - hoverActionsOwnFocus: false, - onCloseRequested: jest.fn(), - onUpdateColumns: jest.fn(), - setClosePopOverTrigger: jest.fn(), -}; - -describe('FieldName', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - test('it renders the field name', () => { - const wrapper = mount( - - - - ); - - expect( - wrapper.find(`[data-test-subj="field-name-${timestampFieldId}"]`).first().text() - ).toEqual(timestampFieldId); - }); - - test('it renders a copy to clipboard action menu item a user hovers over the name', async () => { - const wrapper = mount( - - - - ); - await waitFor(() => { - wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); - wrapper.update(); - jest.runAllTimers(); - wrapper.update(); - expect(wrapper.find('[data-test-subj="hover-actions-copy-button"]').exists()).toBe(true); - }); - }); - - test('it highlights the text specified by the `highlight` prop', () => { - const highlight = 'stamp'; - - const wrapper = mount( - - - - ); - - expect(wrapper.find('mark').first().text()).toEqual(highlight); - }); -}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx deleted file mode 100644 index 6e9672d08b366..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_name.tsx +++ /dev/null @@ -1,165 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiHighlight, EuiText } from '@elastic/eui'; -import React, { useCallback, useState, useMemo, useRef, useContext } from 'react'; -import styled from 'styled-components'; - -import { OnUpdateColumns } from '../timeline/events'; -import { WithHoverActions } from '../../../common/components/with_hover_actions'; -import { ColumnHeaderOptions } from '../../../../common/types'; -import { HoverActions } from '../../../common/components/hover_actions'; -import { TimelineContext } from '../../../../../timelines/public'; - -/** - * The name of a (draggable) field - */ -export const FieldNameContainer = styled.span` - border-radius: 4px; - display: flex; - padding: 0 4px 0 8px; - position: relative; - - &::before { - background-image: linear-gradient( - 135deg, - ${({ theme }) => theme.eui.euiColorMediumShade} 25%, - transparent 25% - ), - linear-gradient(-135deg, ${({ theme }) => theme.eui.euiColorMediumShade} 25%, transparent 25%), - linear-gradient(135deg, transparent 75%, ${({ theme }) => theme.eui.euiColorMediumShade} 75%), - linear-gradient(-135deg, transparent 75%, ${({ theme }) => theme.eui.euiColorMediumShade} 75%); - background-position: 0 0, 1px 0, 1px -1px, 0px 1px; - background-size: 2px 2px; - bottom: 2px; - content: ''; - display: block; - left: 2px; - position: absolute; - top: 2px; - width: 4px; - } - - &:hover, - &:focus { - transition: background-color 0.7s ease; - background-color: #000; - color: #fff; - - &::before { - background-image: linear-gradient(135deg, #fff 25%, transparent 25%), - linear-gradient( - -135deg, - ${({ theme }) => theme.eui.euiColorLightestShade} 25%, - transparent 25% - ), - linear-gradient( - 135deg, - transparent 75%, - ${({ theme }) => theme.eui.euiColorLightestShade} 75% - ), - linear-gradient( - -135deg, - transparent 75%, - ${({ theme }) => theme.eui.euiColorLightestShade} 75% - ); - } - } -`; - -FieldNameContainer.displayName = 'FieldNameContainer'; - -/** Renders a field name in it's non-dragging state */ -export const FieldName = React.memo<{ - categoryId: string; - categoryColumns: ColumnHeaderOptions[]; - closePopOverTrigger: boolean; - fieldId: string; - highlight?: string; - handleClosePopOverTrigger: () => void; - hoverActionsOwnFocus: boolean; - onCloseRequested: () => void; - onUpdateColumns: OnUpdateColumns; -}>( - ({ - closePopOverTrigger, - fieldId, - highlight = '', - handleClosePopOverTrigger, - hoverActionsOwnFocus, - onCloseRequested, - }) => { - const containerRef = useRef(null); - const [showTopN, setShowTopN] = useState(false); - const { timelineId: timelineIdFind } = useContext(TimelineContext); - - const toggleTopN = useCallback(() => { - setShowTopN((prevShowTopN) => { - const newShowTopN = !prevShowTopN; - if (newShowTopN === false) { - handleClosePopOverTrigger(); - } - return newShowTopN; - }); - }, [handleClosePopOverTrigger]); - - const closeTopN = useCallback(() => { - setShowTopN(false); - }, []); - - const hoverContent = useMemo( - () => ( - - ), - [ - closeTopN, - fieldId, - handleClosePopOverTrigger, - hoverActionsOwnFocus, - showTopN, - timelineIdFind, - toggleTopN, - ] - ); - - const render = useCallback( - () => ( - - - - {fieldId} - - - - ), - [fieldId, highlight] - ); - - return ( -
- -
- ); - } -); - -FieldName.displayName = 'FieldName'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.tsx new file mode 100644 index 0000000000000..b060575fdc5cb --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/index.tsx @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import styled from 'styled-components'; +import { + EuiToolTip, + EuiFlexGroup, + EuiFlexItem, + EuiScreenReaderOnly, + EuiHealth, + EuiBadge, + EuiIcon, + EuiText, + EuiHighlight, +} from '@elastic/eui'; +import type { FieldTableColumns } from '../../../../../../timelines/common/types'; +import * as i18n from './translations'; +import { + getExampleText, + getIconFromType, +} from '../../../../common/components/event_details/helpers'; +import { getEmptyValue } from '../../../../common/components/empty_value'; +import { EllipsisText } from '../../../../common/components/truncatable_text'; + +const TypeIcon = styled(EuiIcon)` + margin: 0 4px; + position: relative; + top: -1px; +`; +TypeIcon.displayName = 'TypeIcon'; + +export const Description = styled.span` + user-select: text; + width: 400px; +`; +Description.displayName = 'Description'; + +export const FieldName = React.memo<{ + fieldId: string; + highlight?: string; +}>(({ fieldId, highlight = '' }) => ( + + + {fieldId} + + +)); +FieldName.displayName = 'FieldName'; + +export const getFieldTableColumns = (highlight: string): FieldTableColumns => [ + { + field: 'name', + name: i18n.NAME, + render: (name: string, { type }) => { + return ( + + + + + + + + + + + + ); + }, + sortable: true, + width: '200px', + }, + { + field: 'description', + name: i18n.DESCRIPTION, + render: (description, { name, example }) => ( + + <> + +

{i18n.DESCRIPTION_FOR_FIELD(name)}

+
+ + + {`${description ?? getEmptyValue()} ${getExampleText(example)}`} + + + +
+ ), + sortable: true, + width: '400px', + }, + { + field: 'isRuntime', + name: i18n.RUNTIME, + render: (isRuntime: boolean) => + isRuntime ? : null, + sortable: true, + width: '80px', + }, + { + field: 'category', + name: i18n.CATEGORY, + render: (category: string, { name }) => ( + {category} + ), + sortable: true, + width: '100px', + }, +]; diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/translations.ts new file mode 100644 index 0000000000000..c16307250c2c8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/field_table_columns/translations.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const NAME = i18n.translate('xpack.securitySolution.fieldBrowser.fieldName', { + defaultMessage: 'Name', +}); + +export const DESCRIPTION = i18n.translate('xpack.securitySolution.fieldBrowser.descriptionLabel', { + defaultMessage: 'Description', +}); + +export const DESCRIPTION_FOR_FIELD = (field: string) => + i18n.translate('xpack.securitySolution.fieldBrowser.descriptionForScreenReaderOnly', { + values: { + field, + }, + defaultMessage: 'Description for field {field}:', + }); + +export const CATEGORY = i18n.translate('xpack.securitySolution.fieldBrowser.categoryLabel', { + defaultMessage: 'Category', +}); + +export const RUNTIME = i18n.translate('xpack.securitySolution.fieldBrowser.runtimeLabel', { + defaultMessage: 'Runtime', +}); + +export const RUNTIME_FIELD = i18n.translate('xpack.securitySolution.fieldBrowser.runtimeTitle', { + defaultMessage: 'Runtime Field', +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx new file mode 100644 index 0000000000000..46f2caa147a40 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TimelineId } from '../../../../common/types'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; +import { useCreateFieldButton, CreateFieldEditorActionsRef } from './create_field_button'; +import { getFieldTableColumns } from './field_table_columns'; + +export type { CreateFieldEditorActions } from './create_field_button'; + +export interface UseFieldBrowserOptions { + sourcererScope: SourcererScopeName; + timelineId: TimelineId; + editorActionsRef?: CreateFieldEditorActionsRef; +} + +export const useFieldBrowserOptions = ({ + sourcererScope, + timelineId, + editorActionsRef, +}: UseFieldBrowserOptions) => { + const createFieldButton = useCreateFieldButton(sourcererScope, timelineId, editorActionsRef); + return { + createFieldButton, + getFieldTableColumns, + }; +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/header_actions.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/header_actions.tsx index 9636aadbc08e3..0e26edc6ae1c1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/header_actions.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/header_actions.tsx @@ -86,7 +86,7 @@ const HeaderActionsComponent: React.FC = ({ sort, tabType, timelineId, - createFieldComponent, + fieldBrowserOptions, }) => { const { timelines: timelinesUi } = useKibana().services; const { globalFullScreen, setGlobalFullScreen } = useGlobalFullScreen(); @@ -184,7 +184,7 @@ const HeaderActionsComponent: React.FC = ({ browserFields, columnHeaders, timelineId, - createFieldComponent, + options: fieldBrowserOptions, })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx index aec28732f38af..7e3de3514f5a7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx @@ -28,7 +28,7 @@ import { HeaderActions } from '../actions/header_actions'; jest.mock('../../../../../common/lib/kibana'); const mockUseCreateFieldButton = jest.fn().mockReturnValue(<>); -jest.mock('../../../create_field_button', () => ({ +jest.mock('../../../fields_browser/create_field_button', () => ({ useCreateFieldButton: (...params: unknown[]) => mockUseCreateFieldButton(...params), })); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx index ca1cdef903de8..e58dd520181c1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx @@ -34,7 +34,7 @@ import { Sort } from '../sort'; import { ColumnHeader } from './column_header'; import { SourcererScopeName } from '../../../../../common/store/sourcerer/model'; -import { CreateFieldEditorActions, useCreateFieldButton } from '../../../create_field_button'; +import { useFieldBrowserOptions, CreateFieldEditorActions } from '../../../fields_browser'; export interface ColumnHeadersComponentProps { actionsColumnWidth: number; @@ -190,11 +190,11 @@ export const ColumnHeadersComponent = ({ [trailingControlColumns] ); - const createFieldComponent = useCreateFieldButton( - SourcererScopeName.timeline, - timelineId as TimelineId, - fieldEditorActionsRef - ); + const fieldBrowserOptions = useFieldBrowserOptions({ + sourcererScope: SourcererScopeName.timeline, + timelineId: timelineId as TimelineId, + editorActionsRef: fieldEditorActionsRef, + }); const LeadingHeaderActions = useMemo(() => { return leadingHeaderCells.map( @@ -221,7 +221,7 @@ export const ColumnHeadersComponent = ({ sort={sort} tabType={tabType} timelineId={timelineId} - createFieldComponent={createFieldComponent} + fieldBrowserOptions={fieldBrowserOptions} /> )} @@ -234,7 +234,7 @@ export const ColumnHeadersComponent = ({ actionsColumnWidth, browserFields, columnHeaders, - createFieldComponent, + fieldBrowserOptions, isEventViewer, isSelectAllChecked, onSelectAll, @@ -270,7 +270,7 @@ export const ColumnHeadersComponent = ({ sort={sort} tabType={tabType} timelineId={timelineId} - createFieldComponent={createFieldComponent} + fieldBrowserOptions={fieldBrowserOptions} /> )} @@ -283,7 +283,7 @@ export const ColumnHeadersComponent = ({ actionsColumnWidth, browserFields, columnHeaders, - createFieldComponent, + fieldBrowserOptions, isEventViewer, isSelectAllChecked, onSelectAll, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index f616b4afc2af5..5a9f981988d59 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -114,7 +114,7 @@ jest.mock('../../../../common/lib/helpers/scheduler', () => ({ maxDelay: () => 3000, })); -jest.mock('../../create_field_button', () => ({ +jest.mock('../../fields_browser/create_field_button', () => ({ useCreateFieldButton: () => <>, })); diff --git a/x-pack/plugins/timelines/common/index.ts b/x-pack/plugins/timelines/common/index.ts index 0002dd6eb1432..96728a07432fd 100644 --- a/x-pack/plugins/timelines/common/index.ts +++ b/x-pack/plugins/timelines/common/index.ts @@ -20,7 +20,6 @@ export type { ActionProps, AlertWorkflowStatus, CellValueElementProps, - CreateFieldComponentType, ColumnId, ColumnRenderer, ColumnHeaderType, @@ -28,6 +27,7 @@ export type { ControlColumnProps, DataProvidersAnd, DataProvider, + FieldBrowserOptions, GenericActionRowCellRenderProps, HeaderActionProps, HeaderCellRender, diff --git a/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts b/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts index 0b83cf28f9bb7..544ca033b060c 100644 --- a/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts @@ -11,6 +11,7 @@ import type { IEsSearchRequest, IEsSearchResponse, FieldSpec, + RuntimeField, } from '../../../../../../src/plugins/data/common'; import type { DocValueFields, Maybe } from '../common'; @@ -71,6 +72,7 @@ export interface BrowserField { type: string; subType?: IFieldSubType; readFromDocValues: boolean; + runtimeField?: RuntimeField; } export type BrowserFields = Readonly>>; diff --git a/x-pack/plugins/timelines/common/types/fields_browser/index.ts b/x-pack/plugins/timelines/common/types/fields_browser/index.ts new file mode 100644 index 0000000000000..7aac02be877d2 --- /dev/null +++ b/x-pack/plugins/timelines/common/types/fields_browser/index.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiBasicTableColumn } from '@elastic/eui'; +import { BrowserFields } from '../../search_strategy'; +import { ColumnHeaderOptions } from '../timeline/columns'; + +/** + * An item rendered in the table + */ +export interface BrowserFieldItem { + name: string; + type?: string; + description?: string; + example?: string; + category: string; + selected: boolean; + isRuntime: boolean; +} + +export type OnFieldSelected = (fieldId: string) => void; + +export type CreateFieldComponent = React.FC<{ + onClick: () => void; +}>; +export type FieldTableColumns = Array>; +export type GetFieldTableColumns = (highlight: string) => FieldTableColumns; +export interface FieldBrowserOptions { + createFieldButton?: CreateFieldComponent; + getFieldTableColumns?: GetFieldTableColumns; +} + +export interface FieldBrowserProps { + /** The timeline associated with this field browser */ + timelineId: string; + /** The timeline's current column headers */ + columnHeaders: ColumnHeaderOptions[]; + /** A map of categoryId -> metadata about the fields in that category */ + browserFields: BrowserFields; + /** When true, this Fields Browser is being used as an "events viewer" */ + isEventViewer?: boolean; + /** The options to customize the field browser, supporting columns rendering and button to create fields */ + options?: FieldBrowserOptions; + /** The width of the field browser */ + width?: number; +} diff --git a/x-pack/plugins/timelines/common/types/index.ts b/x-pack/plugins/timelines/common/types/index.ts index 9464a33082a49..f8e2b03063860 100644 --- a/x-pack/plugins/timelines/common/types/index.ts +++ b/x-pack/plugins/timelines/common/types/index.ts @@ -5,4 +5,5 @@ * 2.0. */ +export * from './fields_browser'; export * from './timeline'; diff --git a/x-pack/plugins/timelines/common/types/timeline/actions/index.ts b/x-pack/plugins/timelines/common/types/timeline/actions/index.ts index 0662c63f35edd..6a9c6bf8e74a0 100644 --- a/x-pack/plugins/timelines/common/types/timeline/actions/index.ts +++ b/x-pack/plugins/timelines/common/types/timeline/actions/index.ts @@ -7,11 +7,12 @@ import { ComponentType, JSXElementConstructor } from 'react'; import { EuiDataGridControlColumn, EuiDataGridCellValueElementProps } from '@elastic/eui'; -import { CreateFieldComponentType, OnRowSelected, SortColumnTimeline, TimelineTabs } from '..'; +import { OnRowSelected, SortColumnTimeline, TimelineTabs } from '..'; import { BrowserFields } from '../../../search_strategy/index_fields'; import { ColumnHeaderOptions } from '../columns'; import { TimelineNonEcsData } from '../../../search_strategy'; import { Ecs } from '../../../ecs'; +import { FieldBrowserOptions } from '../../fields_browser'; export interface ActionProps { action?: RowCellRender; @@ -67,7 +68,7 @@ export interface HeaderActionProps { width: number; browserFields: BrowserFields; columnHeaders: ColumnHeaderOptions[]; - createFieldComponent?: CreateFieldComponentType; + fieldBrowserOptions?: FieldBrowserOptions; isEventViewer?: boolean; isSelectAllChecked: boolean; onSelectAll: ({ isSelected }: { isSelected: boolean }) => void; diff --git a/x-pack/plugins/timelines/common/types/timeline/index.ts b/x-pack/plugins/timelines/common/types/timeline/index.ts index 4ebc84a41f4b3..a6c8ed1b74bff 100644 --- a/x-pack/plugins/timelines/common/types/timeline/index.ts +++ b/x-pack/plugins/timelines/common/types/timeline/index.ts @@ -465,10 +465,6 @@ export enum TimelineTabs { eql = 'eql', } -export type CreateFieldComponentType = React.FC<{ - onClick: () => void; -}>; - // eslint-disable-next-line @typescript-eslint/no-explicit-any type EmptyObject = Partial>; diff --git a/x-pack/plugins/timelines/public/components/fields_browser/index.tsx b/x-pack/plugins/timelines/public/components/fields_browser/index.tsx index 31b8e9f62803e..12133cbee303e 100644 --- a/x-pack/plugins/timelines/public/components/fields_browser/index.tsx +++ b/x-pack/plugins/timelines/public/components/fields_browser/index.tsx @@ -9,9 +9,14 @@ import React from 'react'; import type { Store } from 'redux'; import { Provider } from 'react-redux'; import { I18nProvider } from '@kbn/i18n-react'; -import type { FieldBrowserProps } from '../t_grid/toolbar/fields_browser/types'; import { StatefulFieldsBrowser } from '../t_grid/toolbar/fields_browser'; -export type { FieldBrowserProps } from '../t_grid/toolbar/fields_browser/types'; +import { FieldBrowserProps } from '../../../common/types/fields_browser'; +export type { + CreateFieldComponent, + FieldBrowserOptions, + FieldBrowserProps, + GetFieldTableColumns, +} from '../../../common/types/fields_browser'; const EMPTY_BROWSER_FIELDS = {}; diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx index 2ae0146f80f7e..4ba36a3ec6419 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx @@ -47,7 +47,6 @@ import { TimelineTabs, SetEventsLoading, SetEventsDeleted, - CreateFieldComponentType, } from '../../../../common/types/timeline'; import type { TimelineItem, TimelineNonEcsData } from '../../../../common/search_strategy/timeline'; @@ -63,10 +62,11 @@ import { import type { BrowserFields } from '../../../../common/search_strategy/index_fields'; import type { OnRowSelected, OnSelectAll } from '../types'; +import type { FieldBrowserOptions } from '../../../../common/types'; import type { Refetch } from '../../../store/t_grid/inputs'; import { getPageRowIndex } from '../../../../common/utils/pagination'; import { StatefulEventContext } from '../../../components/stateful_event_context'; -import { StatefulFieldsBrowser } from '../../../components/t_grid/toolbar/fields_browser'; +import { StatefulFieldsBrowser } from '../toolbar/fields_browser'; import { tGridActions, TGridModel, tGridSelectors, TimelineState } from '../../../store/t_grid'; import { useDeepEqualSelector } from '../../../hooks/use_selector'; import { RowAction } from './row_action'; @@ -88,10 +88,10 @@ interface OwnProps { appId?: string; browserFields: BrowserFields; bulkActions?: BulkActionsProp; - createFieldComponent?: CreateFieldComponentType; data: TimelineItem[]; defaultCellActions?: TGridCellAction[]; disabledCellActions: string[]; + fieldBrowserOptions?: FieldBrowserOptions; filters?: Filter[]; filterQuery?: string; filterStatus?: AlertStatus; @@ -149,8 +149,8 @@ const EuiDataGridContainer = styled.div<{ hideLastPage: boolean }>` const transformControlColumns = ({ columnHeaders, controlColumns, - createFieldComponent, data, + fieldBrowserOptions, isEventViewer = false, loadingEventIds, onRowSelected, @@ -171,9 +171,9 @@ const transformControlColumns = ({ }: { columnHeaders: ColumnHeaderOptions[]; controlColumns: ControlColumnProps[]; - createFieldComponent?: CreateFieldComponentType; data: TimelineItem[]; disabledCellActions: string[]; + fieldBrowserOptions?: FieldBrowserOptions; isEventViewer?: boolean; loadingEventIds: string[]; onRowSelected: OnRowSelected; @@ -209,6 +209,7 @@ const transformControlColumns = ({ )} @@ -303,10 +303,10 @@ export const BodyComponent = React.memo( bulkActions = true, clearSelected, columnHeaders, - createFieldComponent, data, defaultCellActions, disabledCellActions, + fieldBrowserOptions, filterQuery, filters, filterStatus, @@ -502,7 +502,7 @@ export const BodyComponent = React.memo( @@ -529,6 +529,7 @@ export const BodyComponent = React.memo( id, totalSelectAllAlerts, totalItems, + fieldBrowserOptions, filterStatus, filterQuery, indexNames, @@ -539,7 +540,6 @@ export const BodyComponent = React.memo( additionalControls, browserFields, columnHeaders, - createFieldComponent, ] ); @@ -629,9 +629,9 @@ export const BodyComponent = React.memo( transformControlColumns({ columnHeaders, controlColumns, - createFieldComponent, data, disabledCellActions, + fieldBrowserOptions, isEventViewer, loadingEventIds, onRowSelected, @@ -656,9 +656,9 @@ export const BodyComponent = React.memo( leadingControlColumns, trailingControlColumns, columnHeaders, - createFieldComponent, data, disabledCellActions, + fieldBrowserOptions, isEventViewer, id, loadingEventIds, diff --git a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx index b97e4047d10e7..69c04b31fa44b 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx @@ -21,7 +21,6 @@ import type { CoreStart } from '../../../../../../../src/core/public'; import type { BrowserFields } from '../../../../common/search_strategy/index_fields'; import { BulkActionsProp, - CreateFieldComponentType, TGridCellAction, TimelineId, TimelineTabs, @@ -43,6 +42,7 @@ import { defaultHeaders } from '../body/column_headers/default_headers'; import { buildCombinedQuery, getCombinedFilterQuery, resolverIsShowing } from '../helpers'; import { tGridActions, tGridSelectors } from '../../../store/t_grid'; import { useTimelineEvents, InspectResponse, Refetch } from '../../../container'; +import { FieldBrowserOptions } from '../../fields_browser'; import { StatefulBody } from '../body'; import { SELECTOR_TIMELINE_GLOBAL_CONTAINER, UpdatedFlexGroup, UpdatedFlexItem } from '../styles'; import { Sort } from '../body/sort'; @@ -98,7 +98,6 @@ export interface TGridIntegratedProps { browserFields: BrowserFields; bulkActions?: BulkActionsProp; columns: ColumnHeaderOptions[]; - createFieldComponent?: CreateFieldComponentType; data?: DataPublicPluginStart; dataProviders: DataProvider[]; dataViewId?: string | null; @@ -108,6 +107,7 @@ export interface TGridIntegratedProps { docValueFields: DocValueFields[]; end: string; entityType: EntityType; + fieldBrowserOptions?: FieldBrowserOptions; filters: Filter[]; filterStatus?: AlertStatus; globalFullScreen: boolean; @@ -153,12 +153,12 @@ const TGridIntegratedComponent: React.FC = ({ docValueFields, end, entityType, + fieldBrowserOptions, filters, filterStatus, globalFullScreen, graphEventId, graphOverlay = null, - createFieldComponent, hasAlertsCrud, id, indexNames, @@ -363,10 +363,10 @@ const TGridIntegratedComponent: React.FC = ({ appId={appId} browserFields={browserFields} bulkActions={bulkActions} - createFieldComponent={createFieldComponent} data={nonDeletedEvents} defaultCellActions={defaultCellActions} disabledCellActions={disabledCellActions} + fieldBrowserOptions={fieldBrowserOptions} filterQuery={filterQuery} filters={filters} filterStatus={filterStatus} diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_badges.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_badges.test.tsx new file mode 100644 index 0000000000000..e945f91c47afd --- /dev/null +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_badges.test.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { TestProviders } from '../../../../mock'; + +import { CategoriesBadges } from './categories_badges'; + +const mockSetSelectedCategoryIds = jest.fn(); +const defaultProps = { + setSelectedCategoryIds: mockSetSelectedCategoryIds, + selectedCategoryIds: [], +}; + +describe('CategoriesBadges', () => { + beforeEach(() => { + mockSetSelectedCategoryIds.mockClear(); + }); + + it('should render empty badges', () => { + const result = render( + + + + ); + + const badges = result.getByTestId('category-badges'); + expect(badges).toBeInTheDocument(); + expect(badges.childNodes.length).toBe(0); + }); + + it('should render the selector button with selected categories', () => { + const result = render( + + + + ); + + const badges = result.getByTestId('category-badges'); + expect(badges.childNodes.length).toBe(2); + expect(result.getByTestId('category-badge-base')).toBeInTheDocument(); + expect(result.getByTestId('category-badge-event')).toBeInTheDocument(); + }); + + it('should call the set selected callback when badge unselect button clicked', () => { + const result = render( + + + + ); + + result.getByTestId('category-badge-unselect-base').click(); + expect(mockSetSelectedCategoryIds).toHaveBeenCalledWith(['event']); + }); +}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_badges.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_badges.tsx new file mode 100644 index 0000000000000..14b928d18de45 --- /dev/null +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_badges.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import styled from 'styled-components'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +interface CategoriesBadgesProps { + setSelectedCategoryIds: (categoryIds: string[]) => void; + selectedCategoryIds: string[]; +} + +const CategoriesBadgesGroup = styled(EuiFlexGroup)` + margin-top: ${({ theme }) => theme.eui.euiSizeXS}; + min-height: 24px; +`; +CategoriesBadgesGroup.displayName = 'CategoriesBadgesGroup'; + +const CategoriesBadgesComponent: React.FC = ({ + setSelectedCategoryIds, + selectedCategoryIds, +}) => { + const onUnselectCategory = useCallback( + (categoryId: string) => { + setSelectedCategoryIds( + selectedCategoryIds.filter((selectedCategoryId) => selectedCategoryId !== categoryId) + ); + }, + [setSelectedCategoryIds, selectedCategoryIds] + ); + + return ( + + {selectedCategoryIds.map((categoryId) => ( + + onUnselectCategory(categoryId)} + iconOnClickAriaLabel="unselect category" + data-test-subj={`category-badge-${categoryId}`} + closeButtonProps={{ 'data-test-subj': `category-badge-unselect-${categoryId}` }} + > + {categoryId} + + + ))} + + ); +}; + +export const CategoriesBadges = React.memo(CategoriesBadgesComponent); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_pane.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_pane.test.tsx deleted file mode 100644 index e2f1d78cf5bc2..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_pane.test.tsx +++ /dev/null @@ -1,51 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { mount } from 'enzyme'; -import React from 'react'; - -import { mockBrowserFields } from '../../../../mock'; - -import { CATEGORY_PANE_WIDTH } from './helpers'; -import { CategoriesPane } from './categories_pane'; -import * as i18n from './translations'; - -const timelineId = 'test'; - -describe('CategoriesPane', () => { - test('it renders the expected title', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="categories-pane-title"]').first().text()).toEqual( - i18n.CATEGORIES - ); - }); - - test('it renders a "No fields match" message when filteredBrowserFields is empty', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="categories-container"] tbody').first().text()).toEqual( - i18n.NO_FIELDS_MATCH - ); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_pane.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_pane.tsx deleted file mode 100644 index ffb93aee11b55..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_pane.tsx +++ /dev/null @@ -1,118 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiInMemoryTable, EuiTitle } from '@elastic/eui'; -import { noop } from 'lodash/fp'; -import React, { useCallback, useRef } from 'react'; -import styled from 'styled-components'; -import { - DATA_COLINDEX_ATTRIBUTE, - DATA_ROWINDEX_ATTRIBUTE, - onKeyDownFocusHandler, -} from '../../../../../common/utils/accessibility'; -import type { BrowserFields } from '../../../../../common/search_strategy'; -import { getCategoryColumns } from './category_columns'; -import { CATEGORIES_PANE_CLASS_NAME, TABLE_HEIGHT } from './helpers'; - -import * as i18n from './translations'; - -const CategoryNames = styled.div<{ height: number; width: number }>` - ${({ width }) => `width: ${width}px`}; - ${({ height }) => `height: ${height}px`}; - overflow-y: hidden; - padding: 5px; - thead { - display: none; - } -`; - -CategoryNames.displayName = 'CategoryNames'; - -const Title = styled(EuiTitle)` - padding-left: 5px; -`; - -const H3 = styled.h3` - text-align: left; -`; - -Title.displayName = 'Title'; - -interface Props { - /** - * A map of categoryId -> metadata about the fields in that category, - * filtered such that the name of every field in the category includes - * the filter input (as a substring). - */ - filteredBrowserFields: BrowserFields; - /** - * Invoked when the user clicks on the name of a category in the left-hand - * side of the field browser - */ - onCategorySelected: (categoryId: string) => void; - /** The category selected on the left-hand side of the field browser */ - selectedCategoryId: string; - timelineId: string; - /** The width of the categories pane */ - width: number; -} - -export const CategoriesPane = React.memo( - ({ filteredBrowserFields, onCategorySelected, selectedCategoryId, timelineId, width }) => { - const containerElement = useRef(null); - const onKeyDown = useCallback( - (e: React.KeyboardEvent) => { - onKeyDownFocusHandler({ - colindexAttribute: DATA_COLINDEX_ATTRIBUTE, - containerElement: containerElement?.current, - event: e, - maxAriaColindex: 1, - maxAriaRowindex: Object.keys(filteredBrowserFields).length, - onColumnFocused: noop, - rowindexAttribute: DATA_ROWINDEX_ATTRIBUTE, - }); - }, - [containerElement, filteredBrowserFields] - ); - - return ( - <> - - <H3 data-test-subj="categories-pane-title">{i18n.CATEGORIES}</H3> - - - - ({ categoryId, ariaRowindex: i + 1 }))} - message={i18n.NO_FIELDS_MATCH} - pagination={false} - sorting={false} - tableCaption={i18n.CATEGORIES} - /> - - - ); - } -); - -CategoriesPane.displayName = 'CategoriesPane'; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_selector.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_selector.test.tsx new file mode 100644 index 0000000000000..eff37376a296e --- /dev/null +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_selector.test.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { mockBrowserFields, TestProviders } from '../../../../mock'; + +import { CategoriesSelector } from './categories_selector'; + +const mockSetSelectedCategoryIds = jest.fn(); +const defaultProps = { + filteredBrowserFields: mockBrowserFields, + setSelectedCategoryIds: mockSetSelectedCategoryIds, + selectedCategoryIds: [], +}; + +describe('CategoriesSelector', () => { + beforeEach(() => { + mockSetSelectedCategoryIds.mockClear(); + }); + + it('should render the default selector button', () => { + const categoriesCount = Object.keys(mockBrowserFields).length; + const result = render( + + + + ); + + expect(result.getByTestId('categories-filter-button')).toBeInTheDocument(); + expect(result.getByText('Categories')).toBeInTheDocument(); + expect(result.getByText(categoriesCount)).toBeInTheDocument(); + }); + + it('should render the selector button with selected categories', () => { + const result = render( + + + + ); + + expect(result.getByTestId('categories-filter-button')).toBeInTheDocument(); + expect(result.getByText('Categories')).toBeInTheDocument(); + expect(result.getByText('2')).toBeInTheDocument(); + }); + + it('should open the category selector', () => { + const result = render( + + + + ); + + result.getByTestId('categories-filter-button').click(); + + expect(result.getByTestId('categories-selector-search')).toBeInTheDocument(); + expect(result.getByTestId(`categories-selector-option-base`)).toBeInTheDocument(); + }); + + it('should open the category selector with selected categories', () => { + const result = render( + + + + ); + + result.getByTestId('categories-filter-button').click(); + + expect(result.getByTestId('categories-selector-search')).toBeInTheDocument(); + expect(result.getByTestId(`categories-selector-option-base`)).toBeInTheDocument(); + expect(result.getByTestId(`categories-selector-option-name-base`)).toHaveStyleRule( + 'font-weight', + 'bold' + ); + }); + + it('should call setSelectedCategoryIds when category selected', () => { + const result = render( + + + + ); + + result.getByTestId('categories-filter-button').click(); + result.getByTestId(`categories-selector-option-base`).click(); + expect(mockSetSelectedCategoryIds).toHaveBeenCalledWith(['base']); + }); +}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_selector.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_selector.tsx new file mode 100644 index 0000000000000..6aebd32543ea3 --- /dev/null +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/categories_selector.tsx @@ -0,0 +1,173 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo, useState } from 'react'; +import { omit } from 'lodash'; +import { + EuiFilterButton, + EuiFilterGroup, + EuiFlexGroup, + EuiFlexItem, + EuiHighlight, + EuiPopover, + EuiSelectable, + FilterChecked, +} from '@elastic/eui'; +import { BrowserFields } from '../../../../../common'; +import * as i18n from './translations'; +import { CountBadge, getFieldCount, CategoryName, CategorySelectableContainer } from './helpers'; +import { isEscape } from '../../../../../common/utils/accessibility'; + +interface CategoriesSelectorProps { + /** + * A map of categoryId -> metadata about the fields in that category, + * filtered such that the name of every field in the category includes + * the filter input (as a substring). + */ + filteredBrowserFields: BrowserFields; + /** + * Invoked when the user clicks on the name of a category in the left-hand + * side of the field browser + */ + setSelectedCategoryIds: (categoryIds: string[]) => void; + /** The category selected on the left-hand side of the field browser */ + selectedCategoryIds: string[]; +} + +interface CategoryOption { + label: string; + count: number; + checked?: FilterChecked; +} + +const renderOption = (option: CategoryOption, searchValue: string) => { + const { label, count, checked } = option; + // Some category names have spaces, but test selectors don't like spaces, + // Tests are not able to find subjects with spaces, so we need to clean them. + const idAttr = label.replace(/\s/g, ''); + return ( + + + + {label} + + + + {count} + + + ); +}; + +const CategoriesSelectorComponent: React.FC = ({ + filteredBrowserFields, + setSelectedCategoryIds, + selectedCategoryIds, +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const togglePopover = useCallback(() => { + setIsPopoverOpen((open) => !open); + }, []); + const closePopover = useCallback(() => { + setIsPopoverOpen(false); + }, []); + + const totalCategories = useMemo( + () => Object.keys(filteredBrowserFields).length, + [filteredBrowserFields] + ); + + const categoryOptions: CategoryOption[] = useMemo(() => { + const unselectedCategoryIds = Object.keys( + omit(filteredBrowserFields, selectedCategoryIds) + ).sort(); + return [ + ...selectedCategoryIds.map((categoryId) => ({ + label: categoryId, + count: getFieldCount(filteredBrowserFields[categoryId]), + checked: 'on', + })), + ...unselectedCategoryIds.map((categoryId) => ({ + label: categoryId, + count: getFieldCount(filteredBrowserFields[categoryId]), + })), + ]; + }, [selectedCategoryIds, filteredBrowserFields]); + + const onCategoriesChange = useCallback( + (options: CategoryOption[]) => { + setSelectedCategoryIds( + options.filter(({ checked }) => checked === 'on').map(({ label }) => label) + ); + }, + [setSelectedCategoryIds] + ); + + const onKeyDown = useCallback((keyboardEvent: React.KeyboardEvent) => { + if (isEscape(keyboardEvent)) { + // Prevent escape to close the field browser modal after closing the category selector + keyboardEvent.stopPropagation(); + } + }, []); + + return ( + + 0} + iconType="arrowDown" + isSelected={isPopoverOpen} + numActiveFilters={selectedCategoryIds.length} + numFilters={totalCategories} + onClick={togglePopover} + > + {i18n.CATEGORIES} + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + > + + + {(list, search) => ( + <> + {search} + {list} + + )} + + + + + ); +}; + +export const CategoriesSelector = React.memo(CategoriesSelectorComponent); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category.test.tsx deleted file mode 100644 index 98f02a9484eab..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category.test.tsx +++ /dev/null @@ -1,100 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { useMountAppended } from '../../../utils/use_mount_appended'; - -import { Category } from './category'; -import { getFieldItems } from './field_items'; -import { FIELDS_PANE_WIDTH } from './helpers'; -import { mockBrowserFields, TestProviders } from '../../../../mock'; - -import * as i18n from './translations'; - -describe('Category', () => { - const timelineId = 'test'; - const selectedCategoryId = 'client'; - const mount = useMountAppended(); - - test('it renders the category id as the value of the title', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="selected-category-title"]').first().text()).toEqual( - selectedCategoryId - ); - }); - - test('it renders the Field column header', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('.euiTableCellContent__text').at(1).text()).toEqual(i18n.FIELD); - }); - - test('it renders the Description column header', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('.euiTableCellContent__text').at(2).text()).toEqual(i18n.DESCRIPTION); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category.tsx deleted file mode 100644 index 3130c46aa0684..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category.tsx +++ /dev/null @@ -1,114 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiInMemoryTable } from '@elastic/eui'; -import { noop } from 'lodash/fp'; -import React, { useCallback, useMemo, useRef } from 'react'; -import styled from 'styled-components'; -import { - arrayIndexToAriaIndex, - DATA_COLINDEX_ATTRIBUTE, - DATA_ROWINDEX_ATTRIBUTE, - onKeyDownFocusHandler, -} from '../../../../../common/utils/accessibility'; -import type { BrowserFields } from '../../../../../common/search_strategy'; -import type { OnUpdateColumns } from '../../../../../common/types'; - -import { CategoryTitle } from './category_title'; -import { getFieldColumns } from './field_items'; -import type { FieldItem } from './field_items'; -import { CATEGORY_TABLE_CLASS_NAME, TABLE_HEIGHT } from './helpers'; - -import * as i18n from './translations'; - -const TableContainer = styled.div<{ height: number; width: number }>` - ${({ height }) => `height: ${height}px`}; - ${({ width }) => `width: ${width}px`}; - overflow: hidden; -`; - -TableContainer.displayName = 'TableContainer'; - -/** - * This callback, invoked via `EuiInMemoryTable`'s `rowProps, assigns - * attributes to every ``. - */ -const getAriaRowindex = (fieldItem: FieldItem) => - fieldItem.ariaRowindex != null ? { 'data-rowindex': fieldItem.ariaRowindex } : {}; - -interface Props { - categoryId: string; - fieldItems: FieldItem[]; - filteredBrowserFields: BrowserFields; - onCategorySelected: (categoryId: string) => void; - onUpdateColumns: OnUpdateColumns; - timelineId: string; - width: number; -} - -export const Category = React.memo( - ({ categoryId, filteredBrowserFields, fieldItems, onUpdateColumns, timelineId, width }) => { - const containerElement = useRef(null); - const onKeyDown = useCallback( - (keyboardEvent: React.KeyboardEvent) => { - onKeyDownFocusHandler({ - colindexAttribute: DATA_COLINDEX_ATTRIBUTE, - containerElement: containerElement?.current, - event: keyboardEvent, - maxAriaColindex: 3, - maxAriaRowindex: fieldItems.length, - onColumnFocused: noop, - rowindexAttribute: DATA_ROWINDEX_ATTRIBUTE, - }); - }, - [fieldItems.length] - ); - - const fieldItemsWithRowindex = useMemo( - () => - fieldItems.map((fieldItem, i) => ({ - ...fieldItem, - ariaRowindex: arrayIndexToAriaIndex(i), - })), - [fieldItems] - ); - - const columns = useMemo(() => getFieldColumns(), []); - - return ( - <> - - - - - - - ); - } -); - -Category.displayName = 'Category'; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_columns.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_columns.test.tsx deleted file mode 100644 index a94ffee597c79..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_columns.test.tsx +++ /dev/null @@ -1,153 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { mount } from 'enzyme'; -import React from 'react'; - -import { mockBrowserFields, TestProviders } from '../../../../mock'; - -import { CATEGORY_PANE_WIDTH, getFieldCount, VIEW_ALL_BUTTON_CLASS_NAME } from './helpers'; -import { CategoriesPane } from './categories_pane'; -import { ViewAllButton } from './category_columns'; - -const timelineId = 'test'; - -describe('getCategoryColumns', () => { - Object.keys(mockBrowserFields).forEach((categoryId) => { - test(`it renders the ${categoryId} category name (from filteredBrowserFields)`, () => { - const wrapper = mount( - - ); - - const fieldCount = Object.keys(mockBrowserFields[categoryId].fields ?? {}).length; - - expect( - wrapper.find(`.field-browser-category-pane-${categoryId}-${timelineId}`).first().text() - ).toEqual(`${categoryId}${fieldCount}`); - }); - }); - - Object.keys(mockBrowserFields).forEach((categoryId) => { - test(`it renders the correct field count for the ${categoryId} category (from filteredBrowserFields)`, () => { - const wrapper = mount( - - ); - - expect( - wrapper.find(`[data-test-subj="${categoryId}-category-count"]`).first().text() - ).toEqual(`${getFieldCount(mockBrowserFields[categoryId])}`); - }); - }); - - test('it renders the selected category with bold text', () => { - const selectedCategoryId = 'auditd'; - - const wrapper = mount( - - ); - - expect( - wrapper - .find(`.field-browser-category-pane-${selectedCategoryId}-${timelineId}`) - .find('[data-test-subj="categoryName"]') - .at(1) - ).toHaveStyleRule('font-weight', 'bold', { modifier: '.euiText' }); - }); - - test('it does NOT render an un-selected category with bold text', () => { - const selectedCategoryId = 'auditd'; - const notTheSelectedCategoryId = 'base'; - - const wrapper = mount( - - ); - - expect( - wrapper - .find(`.field-browser-category-pane-${notTheSelectedCategoryId}-${timelineId}`) - .find('[data-test-subj="categoryName"]') - .at(1) - ).toHaveStyleRule('font-weight', 'normal', { modifier: '.euiText' }); - }); - - test('it invokes onCategorySelected when a user clicks a category', () => { - const selectedCategoryId = 'auditd'; - const notTheSelectedCategoryId = 'base'; - - const onCategorySelected = jest.fn(); - - const wrapper = mount( - - ); - - wrapper - .find(`.field-browser-category-pane-${notTheSelectedCategoryId}-${timelineId}`) - .first() - .simulate('click'); - - expect(onCategorySelected).toHaveBeenCalledWith(notTheSelectedCategoryId); - }); -}); - -describe('ViewAllButton', () => { - it(`should update fields with the timestamp and category fields`, () => { - const onUpdateColumns = jest.fn(); - - const wrapper = mount( - - - - ); - - wrapper.find(`.${VIEW_ALL_BUTTON_CLASS_NAME}`).first().simulate('click'); - - expect(onUpdateColumns).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ id: '@timestamp' }), - expect.objectContaining({ id: 'agent.ephemeral_id' }), - expect.objectContaining({ id: 'agent.hostname' }), - expect.objectContaining({ id: 'agent.id' }), - expect.objectContaining({ id: 'agent.name' }), - ]) - ); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_columns.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_columns.tsx deleted file mode 100644 index 0fdf71ff5ffe1..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_columns.tsx +++ /dev/null @@ -1,157 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiButtonIcon, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiText, - EuiToolTip, -} from '@elastic/eui'; -import React, { useCallback, useMemo } from 'react'; -import styled from 'styled-components'; - -import { useDeepEqualSelector } from '../../../../hooks/use_selector'; -import { - LoadingSpinner, - getCategoryPaneCategoryClassName, - getFieldCount, - VIEW_ALL_BUTTON_CLASS_NAME, - CountBadge, -} from './helpers'; -import * as i18n from './translations'; -import { tGridSelectors } from '../../../../store/t_grid'; -import { getColumnsWithTimestamp } from '../../../utils/helpers'; -import type { BrowserFields } from '../../../../../common/search_strategy'; -import type { OnUpdateColumns } from '../../../../../common/types'; - -const CategoryName = styled.span<{ bold: boolean }>` - .euiText { - font-weight: ${({ bold }) => (bold ? 'bold' : 'normal')}; - } -`; - -CategoryName.displayName = 'CategoryName'; - -const LinkContainer = styled.div` - width: 100%; - .euiLink { - width: 100%; - } -`; - -LinkContainer.displayName = 'LinkContainer'; - -const ViewAll = styled(EuiButtonIcon)` - margin-left: 2px; -`; - -ViewAll.displayName = 'ViewAll'; - -export interface CategoryItem { - categoryId: string; -} - -interface ViewAllButtonProps { - categoryId: string; - browserFields: BrowserFields; - onUpdateColumns: OnUpdateColumns; - timelineId: string; -} - -export const ViewAllButton = React.memo( - ({ categoryId, browserFields, onUpdateColumns, timelineId }) => { - const getManageTimeline = useMemo(() => tGridSelectors.getManageTimelineById(), []); - const { isLoading } = useDeepEqualSelector((state) => - getManageTimeline(state, timelineId ?? '') - ); - - const handleClick = useCallback(() => { - onUpdateColumns( - getColumnsWithTimestamp({ - browserFields, - category: categoryId, - }) - ); - }, [browserFields, categoryId, onUpdateColumns]); - - return ( - - {!isLoading ? ( - - ) : ( - - )} - - ); - } -); - -ViewAllButton.displayName = 'ViewAllButton'; - -/** - * Returns the column definition for the (single) column that displays all the - * category names in the field browser */ -export const getCategoryColumns = ({ - filteredBrowserFields, - onCategorySelected, - selectedCategoryId, - timelineId, -}: { - filteredBrowserFields: BrowserFields; - onCategorySelected: (categoryId: string) => void; - selectedCategoryId: string; - timelineId: string; -}) => [ - { - field: 'categoryId', - name: '', - sortable: true, - truncateText: false, - render: ( - categoryId: string, - { ariaRowindex }: { categoryId: string; ariaRowindex: number } - ) => ( - - onCategorySelected(categoryId)} - > - - - - {categoryId} - - - - - - {getFieldCount(filteredBrowserFields[categoryId])} - - - - - - ), - }, -]; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_title.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_title.test.tsx deleted file mode 100644 index 746668491abb8..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_title.test.tsx +++ /dev/null @@ -1,72 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { mount } from 'enzyme'; -import React from 'react'; - -import { mockBrowserFields, TestProviders } from '../../../../mock'; - -import { CategoryTitle } from './category_title'; -import { getFieldCount } from './helpers'; - -describe('CategoryTitle', () => { - const timelineId = 'test'; - - test('it renders the category id as the value of the title', () => { - const categoryId = 'client'; - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="selected-category-title"]').first().text()).toEqual( - categoryId - ); - }); - - test('when `categoryId` specifies a valid category in `filteredBrowserFields`, a count of the field is displayed in the badge', () => { - const validCategoryId = 'client'; - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="selected-category-count-badge"]`).first().text()).toEqual( - `${getFieldCount(mockBrowserFields[validCategoryId])}` - ); - }); - - test('when `categoryId` specifies an INVALID category in `filteredBrowserFields`, a count of zero is displayed in the badge', () => { - const invalidCategoryId = 'this.is.not.happening'; - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="selected-category-count-badge"]`).first().text()).toEqual( - '0' - ); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_title.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_title.tsx deleted file mode 100644 index 0858f30a35246..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/category_title.tsx +++ /dev/null @@ -1,67 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFlexGroup, EuiFlexItem, EuiScreenReaderOnly, EuiTitle } from '@elastic/eui'; -import React from 'react'; - -import { CountBadge, getFieldBrowserCategoryTitleClassName, getFieldCount } from './helpers'; -import type { BrowserFields } from '../../../../../common/search_strategy'; -import type { OnUpdateColumns } from '../../../../../common/types'; - -import { ViewAllButton } from './category_columns'; -import * as i18n from './translations'; - -interface Props { - /** The title of the category */ - categoryId: string; - /** - * A map of categoryId -> metadata about the fields in that category, - * filtered such that the name of every field in the category includes - * the filter input (as a substring). - */ - filteredBrowserFields: BrowserFields; - onUpdateColumns: OnUpdateColumns; - - /** The timeline associated with this field browser */ - timelineId: string; -} - -export const CategoryTitle = React.memo( - ({ filteredBrowserFields, categoryId, onUpdateColumns, timelineId }) => ( - - - -

{i18n.CATEGORY}

-
- -

{categoryId}

-
-
- - - - {getFieldCount(filteredBrowserFields[categoryId])} - - - - - - -
- ) -); - -CategoryTitle.displayName = 'CategoryTitle'; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx index d435d7a280840..ed665155ddcf5 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx @@ -34,18 +34,20 @@ const testProps = { searchInput: '', appliedFilterInput: '', isSearching: false, - onCategorySelected: jest.fn(), + setSelectedCategoryIds: jest.fn(), onHide, onSearchInputChange: jest.fn(), restoreFocusTo: React.createRef(), - selectedCategoryId: '', + selectedCategoryIds: [], timelineId, }; const { storage } = createSecuritySolutionStorageMock(); + describe('FieldsBrowser', () => { beforeEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); }); + test('it renders the Close button', () => { const wrapper = mount( @@ -80,20 +82,7 @@ describe('FieldsBrowser', () => { test('it invokes updateColumns action when the user clicks the Reset Fields button', () => { const wrapper = mount( - ()} - selectedCategoryId={''} - timelineId={timelineId} - /> + ); @@ -129,24 +118,24 @@ describe('FieldsBrowser', () => { expect(wrapper.find('[data-test-subj="field-search"]').exists()).toBe(true); }); - test('it renders the categories pane', () => { + test('it renders the categories selector', () => { const wrapper = mount( ); - expect(wrapper.find('[data-test-subj="left-categories-pane"]').exists()).toBe(true); + expect(wrapper.find('[data-test-subj="categories-selector"]').exists()).toBe(true); }); - test('it renders the fields pane', () => { + test('it renders the fields table', () => { const wrapper = mount( ); - expect(wrapper.find('[data-test-subj="fields-pane"]').exists()).toBe(true); + expect(wrapper.find('[data-test-subj="field-table"]').exists()).toBe(true); }); test('focuses the search input when the component mounts', () => { @@ -183,19 +172,24 @@ describe('FieldsBrowser', () => { expect(onSearchInputChange).toBeCalledWith(inputText); }); - test('does not render the CreateField button when createFieldComponent is provided without a dataViewId', () => { + test('does not render the CreateFieldButton when it is provided but does not have a dataViewId', () => { const MyTestComponent = () =>
{'test'}
; const wrapper = mount( - + ); expect(wrapper.find(MyTestComponent).exists()).toBeFalsy(); }); - test('it renders the CreateField button when createFieldComponent is provided with a dataViewId', () => { + test('it renders the CreateFieldButton when it is provided and have a dataViewId', () => { const state: State = { ...mockGlobalState, timelineById: { @@ -212,7 +206,12 @@ describe('FieldsBrowser', () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.tsx index e55f54e946ad1..5a01c820aa961 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.tsx @@ -17,51 +17,27 @@ import { EuiButtonEmpty, EuiSpacer, } from '@elastic/eui'; -import React, { useEffect, useCallback, useRef, useMemo } from 'react'; -import styled from 'styled-components'; +import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import type { BrowserFields } from '../../../../../common/search_strategy'; -import type { ColumnHeaderOptions, CreateFieldComponentType } from '../../../../../common/types'; -import { - isEscape, - isTab, - stopPropagationAndPreventDefault, -} from '../../../../../common/utils/accessibility'; -import { CategoriesPane } from './categories_pane'; -import { FieldsPane } from './fields_pane'; +import type { FieldBrowserProps, ColumnHeaderOptions } from '../../../../../common/types'; import { Search } from './search'; -import { - CATEGORY_PANE_WIDTH, - CLOSE_BUTTON_CLASS_NAME, - FIELDS_PANE_WIDTH, - FIELD_BROWSER_WIDTH, - focusSearchInput, - onFieldsBrowserTabPressed, - PANES_FLEX_GROUP_WIDTH, - RESET_FIELDS_CLASS_NAME, - scrollCategoriesPane, -} from './helpers'; -import type { FieldBrowserProps } from './types'; +import { CLOSE_BUTTON_CLASS_NAME, FIELD_BROWSER_WIDTH, RESET_FIELDS_CLASS_NAME } from './helpers'; import { tGridActions, tGridSelectors } from '../../../../store/t_grid'; import * as i18n from './translations'; import { useDeepEqualSelector } from '../../../../hooks/use_selector'; +import { CategoriesSelector } from './categories_selector'; +import { FieldTable } from './field_table'; +import { CategoriesBadges } from './categories_badges'; -const PanesFlexGroup = styled(EuiFlexGroup)` - width: ${PANES_FLEX_GROUP_WIDTH}px; -`; -PanesFlexGroup.displayName = 'PanesFlexGroup'; - -type Props = Pick & { +type Props = Pick & { /** * The current timeline column headers */ columnHeaders: ColumnHeaderOptions[]; - - createFieldComponent?: CreateFieldComponentType; - /** * A map of categoryId -> metadata about the fields in that category, * filtered such that the name of every field in the category includes @@ -80,12 +56,12 @@ type Props = Pick & /** * The category selected on the left-hand side of the field browser */ - selectedCategoryId: string; + selectedCategoryIds: string[]; /** * Invoked when the user clicks on the name of a category in the left-hand * side of the field browser */ - onCategorySelected: (categoryId: string) => void; + setSelectedCategoryIds: (categoryIds: string[]) => void; /** * Hides the field browser when invoked */ @@ -110,23 +86,23 @@ type Props = Pick & const FieldsBrowserComponent: React.FC = ({ columnHeaders, filteredBrowserFields, - createFieldComponent: CreateField, isSearching, - onCategorySelected, + setSelectedCategoryIds, onSearchInputChange, onHide, + options, restoreFocusTo, searchInput, appliedFilterInput, - selectedCategoryId, + selectedCategoryIds, timelineId, width = FIELD_BROWSER_WIDTH, }) => { const dispatch = useDispatch(); - const containerElement = useRef(null); const onUpdateColumns = useCallback( - (columns) => dispatch(tGridActions.updateColumns({ id: timelineId, columns })), + (columns: ColumnHeaderOptions[]) => + dispatch(tGridActions.updateColumns({ id: timelineId, columns })), [dispatch, timelineId] ); @@ -156,45 +132,14 @@ const FieldsBrowserComponent: React.FC = ({ [onSearchInputChange] ); - const scrollViewsAndFocusInput = useCallback(() => { - scrollCategoriesPane({ - containerElement: containerElement.current, - selectedCategoryId, - timelineId, - }); - - // always re-focus the input to enable additional filtering - focusSearchInput({ - containerElement: containerElement.current, - timelineId, - }); - }, [selectedCategoryId, timelineId]); - - useEffect(() => { - scrollViewsAndFocusInput(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedCategoryId, timelineId]); - - const onKeyDown = useCallback( - (keyboardEvent: React.KeyboardEvent) => { - if (isEscape(keyboardEvent)) { - stopPropagationAndPreventDefault(keyboardEvent); - closeAndRestoreFocus(); - } else if (isTab(keyboardEvent)) { - onFieldsBrowserTabPressed({ - containerElement: containerElement.current, - keyboardEvent, - selectedCategoryId, - timelineId, - }); - } - }, - [closeAndRestoreFocus, containerElement, selectedCategoryId, timelineId] - ); + const [CreateFieldButton, getFieldTableColumns] = [ + options?.createFieldButton, + options?.getFieldTableColumns, + ]; return ( -
+

{i18n.FIELDS_BROWSER}

@@ -202,11 +147,10 @@ const FieldsBrowserComponent: React.FC = ({
- + = ({ /> - {CreateField && dataViewId != null && dataViewId.length > 0 && ( - + + + + {CreateFieldButton && dataViewId != null && dataViewId.length > 0 && ( + )} + + - - - - - - - - + diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_items.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_items.test.tsx index a4c830c3d8808..45b122354528b 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_items.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_items.test.tsx @@ -5,21 +5,17 @@ * 2.0. */ -import { omit } from 'lodash/fp'; import React from 'react'; -import { waitFor } from '@testing-library/react'; -import { mockBrowserFields, TestProviders } from '../../../../mock'; +import { omit } from 'lodash/fp'; +import { render } from '@testing-library/react'; +import { EuiInMemoryTable } from '@elastic/eui'; +import { mockBrowserFields } from '../../../../mock'; import { defaultColumnHeaderType } from '../../body/column_headers/default_headers'; import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../body/constants'; -import { Category } from './category'; import { getFieldColumns, getFieldItems } from './field_items'; -import { FIELDS_PANE_WIDTH } from './helpers'; -import { useMountAppended } from '../../../utils/use_mount_appended'; import { ColumnHeaderOptions } from '../../../../../common/types'; -const selectedCategoryId = 'base'; -const selectedCategoryFields = mockBrowserFields[selectedCategoryId].fields; const timestampFieldId = '@timestamp'; const columnHeaders: ColumnHeaderOptions[] = [ { @@ -28,7 +24,7 @@ const columnHeaders: ColumnHeaderOptions[] = [ description: 'Date/time when the event originated.\nFor log events this is the date/time when the event was generated, and not when it was read.\nRequired field for all events.', example: '2016-05-23T08:05:34.853Z', - id: '@timestamp', + id: timestampFieldId, type: 'date', aggregatable: true, initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH, @@ -36,295 +32,199 @@ const columnHeaders: ColumnHeaderOptions[] = [ ]; describe('field_items', () => { - const timelineId = 'test'; - const mount = useMountAppended(); - describe('getFieldItems', () => { - Object.keys(selectedCategoryFields!).forEach((fieldId) => { - test(`it renders the name of the ${fieldId} field`, () => { - const wrapper = mount( - - - - ); + const timestampField = mockBrowserFields.base.fields![timestampFieldId]; - expect(wrapper.find(`[data-test-subj="field-name-${fieldId}"]`).first().text()).toEqual( - fieldId - ); + it('should return browser field item format', () => { + const fieldItems = getFieldItems({ + selectedCategoryIds: ['base'], + browserFields: { base: { fields: { [timestampFieldId]: timestampField } } }, + columnHeaders: [], }); - }); - Object.keys(selectedCategoryFields!).forEach((fieldId) => { - test(`it renders a checkbox for the ${fieldId} field`, () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="field-${fieldId}-checkbox"]`).first().exists()).toBe( - true - ); + expect(fieldItems[0]).toEqual({ + name: timestampFieldId, + description: timestampField.description, + category: 'base', + selected: false, + type: timestampField.type, + example: timestampField.example, + isRuntime: false, }); }); - test('it renders a checkbox in the checked state when the field is selected to be displayed as a column in the timeline', () => { - const wrapper = mount( - - - - ); - - expect( - wrapper.find(`[data-test-subj="field-${timestampFieldId}-checkbox"]`).first().props() - .checked - ).toBe(true); - }); - - test('it does NOT render a checkbox in the checked state when the field is NOT selected to be displayed as a column in the timeline', () => { - const wrapper = mount( - - header.id !== timestampFieldId), - highlight: '', - timelineId, - toggleColumn: jest.fn(), - })} - width={FIELDS_PANE_WIDTH} - onCategorySelected={jest.fn()} - onUpdateColumns={jest.fn()} - timelineId={timelineId} - /> - - ); - - expect( - wrapper.find(`[data-test-subj="field-${timestampFieldId}-checkbox"]`).first().props() - .checked - ).toBe(false); - }); - - test('it invokes `toggleColumn` when the user interacts with the checkbox', () => { - const toggleColumn = jest.fn(); - - const wrapper = mount( - - - - ); - - wrapper - .find('input[type="checkbox"]') - .first() - .simulate('change', { - target: { checked: true }, - }); - wrapper.update(); + it('should return selected item', () => { + const fieldItems = getFieldItems({ + selectedCategoryIds: ['base'], + browserFields: { base: { fields: { [timestampFieldId]: timestampField } } }, + columnHeaders, + }); - expect(toggleColumn).toBeCalledWith({ - columnHeaderType: 'not-filtered', - id: '@timestamp', - initialWidth: 180, + expect(fieldItems[0]).toMatchObject({ + selected: true, }); }); - test('it returns the expected signal column settings', async () => { - const mockSelectedCategoryId = 'signal'; - const mockBrowserFieldsWithSignal = { - ...mockBrowserFields, - signal: { - fields: { - 'signal.rule.name': { - aggregatable: true, - category: 'signal', - description: 'rule name', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'signal.rule.name', - searchable: true, - type: 'string', + it('should return isRuntime field', () => { + const fieldItems = getFieldItems({ + selectedCategoryIds: ['base'], + browserFields: { + base: { + fields: { + [timestampFieldId]: { + ...timestampField, + runtimeField: { type: 'keyword', script: { source: 'scripts are fun' } }, + }, }, }, }, - }; - const toggleColumn = jest.fn(); - const wrapper = mount( - - - - ); - wrapper - .find(`[data-test-subj="field-signal.rule.name-checkbox"]`) - .last() - .simulate('change', { - target: { checked: true }, - }); + columnHeaders, + }); - await waitFor(() => { - expect(toggleColumn).toBeCalledWith({ - columnHeaderType: 'not-filtered', - id: 'signal.rule.name', - initialWidth: 180, - }); + expect(fieldItems[0]).toMatchObject({ + isRuntime: true, }); }); - test('it renders the expected icon for a field', () => { - const wrapper = mount( - - - + it('should return all field items of all categories if no category selected', () => { + const fieldCount = Object.values(mockBrowserFields).reduce( + (total, { fields }) => total + Object.keys(fields ?? {}).length, + 0 ); - expect( - wrapper.find(`[data-test-subj="field-${timestampFieldId}-icon"]`).first().props().type - ).toEqual('clock'); + const fieldItems = getFieldItems({ + selectedCategoryIds: [], + browserFields: mockBrowserFields, + columnHeaders: [], + }); + + expect(fieldItems.length).toBe(fieldCount); }); - test('it renders the expected field description', () => { - const wrapper = mount( - - - + it('should return filtered field items of selected categories', () => { + const selectedCategoryIds = ['base', 'event']; + const fieldCount = selectedCategoryIds.reduce( + (total, selectedCategoryId) => + total + Object.keys(mockBrowserFields[selectedCategoryId].fields ?? {}).length, + 0 ); - expect( - wrapper.find(`[data-test-subj="field-${timestampFieldId}-description"]`).first().text() - ).toEqual( - 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events. Example: 2016-05-23T08:05:34.853Z' - ); + const fieldItems = getFieldItems({ + selectedCategoryIds, + browserFields: mockBrowserFields, + columnHeaders: [], + }); + + expect(fieldItems.length).toBe(fieldCount); }); }); describe('getFieldColumns', () => { - test('it returns the expected column definitions', () => { - expect(getFieldColumns().map((column) => omit('render', column))).toEqual([ + const onToggleColumn = jest.fn(); + + beforeEach(() => { + onToggleColumn.mockClear(); + }); + + it('should return default field columns', () => { + expect(getFieldColumns({ onToggleColumn }).map((column) => omit('render', column))).toEqual([ { - field: 'checkbox', + field: 'selected', name: '', sortable: false, width: '25px', }, - { field: 'field', name: 'Field', sortable: false, width: '225px' }, + { + field: 'name', + name: 'Name', + sortable: true, + width: '225px', + }, { field: 'description', name: 'Description', + sortable: true, + width: '400px', + }, + { + field: 'category', + name: 'Category', + sortable: true, + width: '100px', + }, + ]); + }); + + it('should return custom field columns', () => { + const customColumns = [ + { + field: 'name', + name: 'customColumn1', sortable: false, - truncateText: true, + width: '225px', + }, + { + field: 'description', + name: 'customColumn2', + sortable: true, width: '400px', }, + ]; + + expect( + getFieldColumns({ + onToggleColumn, + getFieldTableColumns: () => customColumns, + }).map((column) => omit('render', column)) + ).toEqual([ + { + field: 'selected', + name: '', + sortable: false, + width: '25px', + }, + ...customColumns, ]); }); + + it('should render default columns', () => { + const timestampField = mockBrowserFields.base.fields![timestampFieldId]; + const fieldItems = getFieldItems({ + selectedCategoryIds: ['base'], + browserFields: { base: { fields: { [timestampFieldId]: timestampField } } }, + columnHeaders: [], + }); + + const columns = getFieldColumns({ onToggleColumn }); + const { getByTestId, getAllByText } = render( + + ); + + expect(getAllByText('Name').at(0)).toBeInTheDocument(); + expect(getAllByText('Description').at(0)).toBeInTheDocument(); + expect(getAllByText('Category').at(0)).toBeInTheDocument(); + + expect(getByTestId(`field-${timestampFieldId}-checkbox`)).toBeInTheDocument(); + expect(getByTestId(`field-${timestampFieldId}-name`)).toBeInTheDocument(); + expect(getByTestId(`field-${timestampFieldId}-description`)).toBeInTheDocument(); + expect(getByTestId(`field-${timestampFieldId}-category`)).toBeInTheDocument(); + }); + + it('should call call toggle callback on checkbox click', () => { + const timestampField = mockBrowserFields.base.fields![timestampFieldId]; + const fieldItems = getFieldItems({ + selectedCategoryIds: ['base'], + browserFields: { base: { fields: { [timestampFieldId]: timestampField } } }, + columnHeaders: [], + }); + + const columns = getFieldColumns({ onToggleColumn }); + const { getByTestId } = render( + + ); + + getByTestId(`field-${timestampFieldId}-checkbox`).click(); + expect(onToggleColumn).toHaveBeenCalledWith(timestampFieldId); + }); }); }); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_items.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_items.tsx index a979e209bf64a..1e066eb2174a5 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_items.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_items.tsx @@ -13,14 +13,22 @@ import { EuiFlexGroup, EuiFlexItem, EuiScreenReaderOnly, + EuiBadge, + EuiBasicTableColumn, + EuiTableActionsColumnType, } from '@elastic/eui'; import { uniqBy } from 'lodash/fp'; import styled from 'styled-components'; import { getEmptyValue } from '../../../empty_value'; import { getExampleText, getIconFromType } from '../../../utils/helpers'; -import type { BrowserField } from '../../../../../common/search_strategy'; -import type { ColumnHeaderOptions } from '../../../../../common/types'; +import type { BrowserFields } from '../../../../../common/search_strategy'; +import type { + ColumnHeaderOptions, + BrowserFieldItem, + FieldTableColumns, + GetFieldTableColumns, +} from '../../../../../common/types'; import { defaultColumnHeaderType } from '../../body/column_headers/default_headers'; import { DEFAULT_COLUMN_MIN_WIDTH } from '../../body/constants'; import { TruncatableText } from '../../../truncatable_text'; @@ -33,125 +41,155 @@ const TypeIcon = styled(EuiIcon)` position: relative; top: -1px; `; - TypeIcon.displayName = 'TypeIcon'; export const Description = styled.span` user-select: text; width: 400px; `; - Description.displayName = 'Description'; /** - * An item rendered in the table - */ -export interface FieldItem { - ariaRowindex?: number; - checkbox: React.ReactNode; - description: React.ReactNode; - field: React.ReactNode; - fieldId: string; -} - -/** - * Returns the fields items, values, and descriptions shown when a user expands an event + * Returns the field items of all categories selected */ export const getFieldItems = ({ - category, + browserFields, + selectedCategoryIds, columnHeaders, - highlight = '', - timelineId, - toggleColumn, }: { - category: Partial; + browserFields: BrowserFields; + selectedCategoryIds: string[]; columnHeaders: ColumnHeaderOptions[]; - highlight?: string; - timelineId: string; - toggleColumn: (column: ColumnHeaderOptions) => void; -}): FieldItem[] => - uniqBy('name', [ - ...Object.values(category != null && category.fields != null ? category.fields : {}), - ]).map((field) => ({ - checkbox: ( - - c.id === field.name) !== -1} - data-test-subj={`field-${field.name}-checkbox`} - data-colindex={1} - id={field.name ?? ''} - onChange={() => - toggleColumn({ - columnHeaderType: defaultColumnHeaderType, - id: field.name ?? '', - initialWidth: DEFAULT_COLUMN_MIN_WIDTH, - ...getAlertColumnHeader(timelineId, field.name ?? ''), - }) - } - /> - - ), - field: ( - - - - - - +}): BrowserFieldItem[] => { + const categoryIds = + selectedCategoryIds.length > 0 ? selectedCategoryIds : Object.keys(browserFields); + const selectedFieldIds = new Set(columnHeaders.map(({ id }) => id)); - - - - + return uniqBy( + 'name', + categoryIds.reduce((fieldItems, categoryId) => { + const categoryBrowserFields = Object.values(browserFields[categoryId]?.fields ?? {}); + if (categoryBrowserFields.length > 0) { + fieldItems.push( + ...categoryBrowserFields.map(({ name = '', ...field }) => ({ + name, + type: field.type, + description: field.description ?? '', + example: field.example?.toString(), + category: categoryId, + selected: selectedFieldIds.has(name), + isRuntime: !!field.runtimeField, + })) + ); + } + return fieldItems; + }, []) + ); +}; + +/** + * Returns the column header for a field + */ +export const getColumnHeader = (timelineId: string, fieldName: string): ColumnHeaderOptions => ({ + columnHeaderType: defaultColumnHeaderType, + id: fieldName, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + ...getAlertColumnHeader(timelineId, fieldName), +}); + +const getDefaultFieldTableColumns = (highlight: string): FieldTableColumns => [ + { + field: 'name', + name: i18n.NAME, + render: (name: string, { type }) => { + return ( + + + + + + + + + + + + ); + }, + sortable: true, + width: '225px', + }, + { + field: 'description', + name: i18n.DESCRIPTION, + render: (description: string, { name, example }) => ( + + <> + +

{i18n.DESCRIPTION_FOR_FIELD(name)}

+
+ + + {`${description ?? getEmptyValue()} ${getExampleText(example)}`} + + + +
), - description: ( -
- - <> - -

{i18n.DESCRIPTION_FOR_FIELD(field.name ?? '')}

-
- - - {`${field.description ?? getEmptyValue()} ${getExampleText(field.example)}`} - - - -
-
+ sortable: true, + width: '400px', + }, + { + field: 'category', + name: i18n.CATEGORY, + render: (category: string, { name }) => ( + {category} ), - fieldId: field.name ?? '', - })); + sortable: true, + width: '100px', + }, +]; /** * Returns a table column template provided to the `EuiInMemoryTable`'s * `columns` prop */ -export const getFieldColumns = () => [ +export const getFieldColumns = ({ + onToggleColumn, + highlight = '', + getFieldTableColumns, +}: { + onToggleColumn: (id: string) => void; + highlight?: string; + getFieldTableColumns?: GetFieldTableColumns; +}): FieldTableColumns => [ { - field: 'checkbox', + field: 'selected', name: '', - render: (checkbox: React.ReactNode, _: FieldItem) => checkbox, + render: (selected: boolean, { name }) => ( + + onToggleColumn(name)} + /> + + ), sortable: false, width: '25px', }, - { - field: 'field', - name: i18n.FIELD, - render: (field: React.ReactNode, _: FieldItem) => field, - sortable: false, - width: '225px', - }, - { - field: 'description', - name: i18n.DESCRIPTION, - render: (description: React.ReactNode, _: FieldItem) => description, - sortable: false, - truncateText: true, - width: '400px', - }, + ...(getFieldTableColumns + ? getFieldTableColumns(highlight) + : getDefaultFieldTableColumns(highlight)), ]; + +/** Returns whether the table column has actions attached to it */ +export const isActionsColumn = (column: EuiBasicTableColumn): boolean => { + return !!(column as EuiTableActionsColumnType).actions?.length; +}; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_name.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_name.test.tsx index 05f093eaf1805..6bda5873edc25 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_name.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_name.test.tsx @@ -43,7 +43,7 @@ describe('FieldName', () => { ); expect( - wrapper.find(`[data-test-subj="field-name-${timestampFieldId}"]`).first().text() + wrapper.find(`[data-test-subj="field-${timestampFieldId}-name"]`).first().text() ).toEqual(timestampFieldId); }); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_name.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_name.tsx index 5781211058d3c..0ef0ce64c637b 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_name.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_name.tsx @@ -15,7 +15,7 @@ export const FieldName = React.memo<{ }>(({ fieldId, highlight = '' }) => { return ( - + {fieldId} diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.test.tsx new file mode 100644 index 0000000000000..14f2151d24074 --- /dev/null +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.test.tsx @@ -0,0 +1,225 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { mockBrowserFields, TestProviders } from '../../../../mock'; +import { tGridActions } from '../../../../store/t_grid'; +import { defaultColumnHeaderType } from '../../body/column_headers/default_headers'; +import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../body/constants'; + +import { ColumnHeaderOptions } from '../../../../../common'; +import { FieldTable } from './field_table'; + +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +const timestampFieldId = '@timestamp'; + +const columnHeaders: ColumnHeaderOptions[] = [ + { + category: 'base', + columnHeaderType: defaultColumnHeaderType, + description: + 'Date/time when the event originated.\nFor log events this is the date/time when the event was generated, and not when it was read.\nRequired field for all events.', + example: '2016-05-23T08:05:34.853Z', + id: timestampFieldId, + type: 'date', + aggregatable: true, + initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH, + }, +]; + +describe('FieldTable', () => { + const timelineId = 'test'; + const timestampField = mockBrowserFields.base.fields![timestampFieldId]; + const defaultPageSize = 10; + const totalFields = Object.values(mockBrowserFields).reduce( + (total, { fields }) => total + Object.keys(fields ?? {}).length, + 0 + ); + + beforeEach(() => { + mockDispatch.mockClear(); + }); + + it('should render empty field table', () => { + const result = render( + + + + ); + + expect(result.getByText('No items found')).toBeInTheDocument(); + expect(result.getByTestId('fields-count').textContent).toContain('0'); + }); + + it('should render field table with fields of all categories', () => { + const result = render( + + + + ); + + expect(result.container.getElementsByClassName('euiTableRow').length).toBe(defaultPageSize); + expect(result.getByTestId('fields-count').textContent).toContain(totalFields); + }); + + it('should render field table with fields of categories selected', () => { + const selectedCategoryIds = ['client', 'event']; + + const fieldCount = selectedCategoryIds.reduce( + (total, selectedCategoryId) => + total + Object.keys(mockBrowserFields[selectedCategoryId].fields ?? {}).length, + 0 + ); + + const result = render( + + + + ); + + expect(result.container.getElementsByClassName('euiTableRow').length).toBe(fieldCount); + expect(result.getByTestId('fields-count').textContent).toContain(fieldCount); + }); + + it('should render field table with custom columns', () => { + const fieldTableColumns = [ + { + field: 'name', + name: 'Custom column', + render: () =>
, + }, + ]; + + const result = render( + + fieldTableColumns} + selectedCategoryIds={[]} + columnHeaders={[]} + filteredBrowserFields={mockBrowserFields} + searchInput="" + timelineId={timelineId} + /> + + ); + + expect(result.getByTestId('fields-count').textContent).toContain(totalFields); + expect(result.getAllByText('Custom column').length).toBeGreaterThan(0); + expect(result.getAllByTestId('customColumn').length).toEqual(defaultPageSize); + }); + + it('should render field table with unchecked field', () => { + const result = render( + + + + ); + + const checkbox = result.getByTestId(`field-${timestampFieldId}-checkbox`); + expect(checkbox).not.toHaveAttribute('checked'); + }); + + it('should render field table with checked field', () => { + const result = render( + + + + ); + + const checkbox = result.getByTestId(`field-${timestampFieldId}-checkbox`); + expect(checkbox).toHaveAttribute('checked'); + }); + + it('should dispatch remove column action on field unchecked', () => { + const result = render( + + + + ); + + result.getByTestId(`field-${timestampFieldId}-checkbox`).click(); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith( + tGridActions.removeColumn({ id: timelineId, columnId: timestampFieldId }) + ); + }); + + it('should dispatch upsert column action on field checked', () => { + const result = render( + + + + ); + + result.getByTestId(`field-${timestampFieldId}-checkbox`).click(); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith( + tGridActions.upsertColumn({ + id: timelineId, + column: { + columnHeaderType: defaultColumnHeaderType, + id: timestampFieldId, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + index: 1, + }) + ); + }); +}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.tsx new file mode 100644 index 0000000000000..332422ed664f6 --- /dev/null +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_table.tsx @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import styled from 'styled-components'; +import { EuiInMemoryTable, EuiText } from '@elastic/eui'; +import { useDispatch } from 'react-redux'; +import { BrowserFields, ColumnHeaderOptions } from '../../../../../common'; +import * as i18n from './translations'; +import { getColumnHeader, getFieldColumns, getFieldItems, isActionsColumn } from './field_items'; +import { CATEGORY_TABLE_CLASS_NAME, TABLE_HEIGHT } from './helpers'; +import { tGridActions } from '../../../../store/t_grid'; +import type { GetFieldTableColumns } from '../../../../../common/types/fields_browser'; + +interface FieldTableProps { + timelineId: string; + columnHeaders: ColumnHeaderOptions[]; + /** + * A map of categoryId -> metadata about the fields in that category, + * filtered such that the name of every field in the category includes + * the filter input (as a substring). + */ + filteredBrowserFields: BrowserFields; + /** + * Optional function to customize field table columns + */ + getFieldTableColumns?: GetFieldTableColumns; + /** + * The category selected on the left-hand side of the field browser + */ + selectedCategoryIds: string[]; + /** The text displayed in the search input */ + /** Invoked when a user chooses to view a new set of columns in the timeline */ + searchInput: string; +} + +const TableContainer = styled.div<{ height: number }>` + margin-top: ${({ theme }) => theme.eui.euiSizeXS}; + border-top: ${({ theme }) => theme.eui.euiBorderThin}; + ${({ height }) => `height: ${height}px`}; + overflow: hidden; +`; +TableContainer.displayName = 'TableContainer'; + +const Count = styled.span` + font-weight: bold; +`; +Count.displayName = 'Count'; + +const FieldTableComponent: React.FC = ({ + columnHeaders, + filteredBrowserFields, + getFieldTableColumns, + searchInput, + selectedCategoryIds, + timelineId, +}) => { + const dispatch = useDispatch(); + + const fieldItems = useMemo( + () => + getFieldItems({ + browserFields: filteredBrowserFields, + selectedCategoryIds, + columnHeaders, + }), + [columnHeaders, filteredBrowserFields, selectedCategoryIds] + ); + + const onToggleColumn = useCallback( + (fieldId: string) => { + if (columnHeaders.some(({ id }) => id === fieldId)) { + dispatch( + tGridActions.removeColumn({ + columnId: fieldId, + id: timelineId, + }) + ); + } else { + dispatch( + tGridActions.upsertColumn({ + column: getColumnHeader(timelineId, fieldId), + id: timelineId, + index: 1, + }) + ); + } + }, + [columnHeaders, dispatch, timelineId] + ); + + const columns = useMemo( + () => getFieldColumns({ highlight: searchInput, onToggleColumn, getFieldTableColumns }), + [onToggleColumn, searchInput, getFieldTableColumns] + ); + const hasActions = useMemo(() => columns.some((column) => isActionsColumn(column)), [columns]); + + return ( + <> + + {i18n.FIELDS_SHOWING} + {fieldItems.length} + {i18n.FIELDS_COUNT(fieldItems.length)} + + + + + + + ); +}; + +export const FieldTable = React.memo(FieldTableComponent); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.test.tsx deleted file mode 100644 index aec21b4847136..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.test.tsx +++ /dev/null @@ -1,112 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { useMountAppended } from '../../../utils/use_mount_appended'; -import { mockBrowserFields, TestProviders } from '../../../../mock'; - -import { FIELDS_PANE_WIDTH } from './helpers'; -import { FieldsPane } from './fields_pane'; - -const timelineId = 'test'; - -describe('FieldsPane', () => { - const mount = useMountAppended(); - - test('it renders the selected category', () => { - const selectedCategory = 'auditd'; - - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="selected-category-title"]`).first().text()).toEqual( - selectedCategory - ); - }); - - test('it renders a unknown category that does not exist in filteredBrowserFields', () => { - const selectedCategory = 'unknown'; - - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="selected-category-title"]`).first().text()).toEqual( - selectedCategory - ); - }); - - test('it renders the expected message when `filteredBrowserFields` is empty and `searchInput` is empty', () => { - const searchInput = ''; - - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="no-fields-match"]`).first().text()).toEqual( - 'No fields match ' - ); - }); - - test('it renders the expected message when `filteredBrowserFields` is empty and `searchInput` is an unknown field name', () => { - const searchInput = 'thisFieldDoesNotExist'; - - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="no-fields-match"]`).first().text()).toEqual( - `No fields match ${searchInput}` - ); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.tsx deleted file mode 100644 index d1d0254d0c917..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/fields_pane.tsx +++ /dev/null @@ -1,145 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React, { useCallback, useMemo } from 'react'; -import styled from 'styled-components'; -import { useDispatch } from 'react-redux'; - -import { Category } from './category'; -import { getFieldItems } from './field_items'; -import { FIELDS_PANE_WIDTH, TABLE_HEIGHT } from './helpers'; - -import * as i18n from './translations'; -import type { BrowserFields } from '../../../../../common/search_strategy'; -import type { ColumnHeaderOptions, OnUpdateColumns } from '../../../../../common/types'; -import { tGridActions } from '../../../../store/t_grid'; - -const NoFieldsPanel = styled.div` - background-color: ${(props) => props.theme.eui.euiColorLightestShade}; - width: ${FIELDS_PANE_WIDTH}px; - height: ${TABLE_HEIGHT}px; -`; - -NoFieldsPanel.displayName = 'NoFieldsPanel'; - -const NoFieldsFlexGroup = styled(EuiFlexGroup)` - height: 100%; -`; - -NoFieldsFlexGroup.displayName = 'NoFieldsFlexGroup'; - -interface Props { - timelineId: string; - columnHeaders: ColumnHeaderOptions[]; - /** - * A map of categoryId -> metadata about the fields in that category, - * filtered such that the name of every field in the category includes - * the filter input (as a substring). - */ - filteredBrowserFields: BrowserFields; - /** - * Invoked when the user clicks on the name of a category in the left-hand - * side of the field browser - */ - onCategorySelected: (categoryId: string) => void; - /** The text displayed in the search input */ - /** Invoked when a user chooses to view a new set of columns in the timeline */ - onUpdateColumns: OnUpdateColumns; - searchInput: string; - /** - * The category selected on the left-hand side of the field browser - */ - selectedCategoryId: string; - /** The width field browser */ - width: number; -} -export const FieldsPane = React.memo( - ({ - columnHeaders, - filteredBrowserFields, - onCategorySelected, - onUpdateColumns, - searchInput, - selectedCategoryId, - timelineId, - width, - }) => { - const dispatch = useDispatch(); - - const toggleColumn = useCallback( - (column: ColumnHeaderOptions) => { - if (columnHeaders.some((c) => c.id === column.id)) { - dispatch( - tGridActions.removeColumn({ - columnId: column.id, - id: timelineId, - }) - ); - } else { - dispatch( - tGridActions.upsertColumn({ - column, - id: timelineId, - index: 1, - }) - ); - } - }, - [columnHeaders, dispatch, timelineId] - ); - - const filteredBrowserFieldsExists = useMemo( - () => Object.keys(filteredBrowserFields).length > 0, - [filteredBrowserFields] - ); - - const fieldItems = useMemo(() => { - return getFieldItems({ - category: filteredBrowserFields[selectedCategoryId], - columnHeaders, - highlight: searchInput, - timelineId, - toggleColumn, - }); - }, [ - columnHeaders, - filteredBrowserFields, - searchInput, - selectedCategoryId, - timelineId, - toggleColumn, - ]); - - if (filteredBrowserFieldsExists) { - return ( - - ); - } - - return ( - - - -

{i18n.NO_FIELDS_MATCH_INPUT(searchInput)}

-
-
-
- ); - } -); - -FieldsPane.displayName = 'FieldsPane'; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.test.tsx index 239d7c726e286..ad90956013e41 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.test.tsx @@ -10,45 +10,12 @@ import { mockBrowserFields } from '../../../../mock'; import { categoryHasFields, createVirtualCategory, - getCategoryPaneCategoryClassName, - getFieldBrowserCategoryTitleClassName, - getFieldBrowserSearchInputClassName, getFieldCount, filterBrowserFieldsByFieldName, } from './helpers'; import { BrowserFields } from '../../../../../common/search_strategy'; -const timelineId = 'test'; - describe('helpers', () => { - describe('getCategoryPaneCategoryClassName', () => { - test('it returns the expected class name', () => { - const categoryId = 'auditd'; - - expect(getCategoryPaneCategoryClassName({ categoryId, timelineId })).toEqual( - 'field-browser-category-pane-auditd-test' - ); - }); - }); - - describe('getFieldBrowserCategoryTitleClassName', () => { - test('it returns the expected class name', () => { - const categoryId = 'auditd'; - - expect(getFieldBrowserCategoryTitleClassName({ categoryId, timelineId })).toEqual( - 'field-browser-category-title-auditd-test' - ); - }); - }); - - describe('getFieldBrowserSearchInputClassName', () => { - test('it returns the expected class name', () => { - expect(getFieldBrowserSearchInputClassName(timelineId)).toEqual( - 'field-browser-search-input-test' - ); - }); - }); - describe('categoryHasFields', () => { test('it returns false if the category fields property is undefined', () => { expect(categoryHasFields({})).toBe(false); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx index 5406940aab3e9..21829bda265e1 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx @@ -9,11 +9,6 @@ import { EuiBadge, EuiLoadingSpinner } from '@elastic/eui'; import { filter, get, pickBy } from 'lodash/fp'; import styled from 'styled-components'; -import { - elementOrChildrenHasFocus, - skipFocusInContainerTo, - stopPropagationAndPreventDefault, -} from '../../../../../common/utils/accessibility'; import { TimelineId } from '../../../../../public/types'; import type { BrowserField, BrowserFields } from '../../../../../common/search_strategy'; import { defaultHeaders } from '../../../../store/t_grid/defaults'; @@ -27,44 +22,8 @@ export const LoadingSpinner = styled(EuiLoadingSpinner)` LoadingSpinner.displayName = 'LoadingSpinner'; -export const CATEGORY_PANE_WIDTH = 200; -export const DESCRIPTION_COLUMN_WIDTH = 300; -export const FIELD_COLUMN_WIDTH = 200; export const FIELD_BROWSER_WIDTH = 925; -export const FIELDS_PANE_WIDTH = 670; -export const HEADER_HEIGHT = 40; -export const PANES_FLEX_GROUP_WIDTH = CATEGORY_PANE_WIDTH + FIELDS_PANE_WIDTH + 10; -export const PANES_FLEX_GROUP_HEIGHT = 260; export const TABLE_HEIGHT = 260; -export const TYPE_COLUMN_WIDTH = 50; - -/** - * Returns the CSS class name for the title of a category shown in the left - * side field browser - */ -export const getCategoryPaneCategoryClassName = ({ - categoryId, - timelineId, -}: { - categoryId: string; - timelineId: string; -}): string => `field-browser-category-pane-${categoryId}-${timelineId}`; - -/** - * Returns the CSS class name for the title of a category shown in the right - * side of field browser - */ -export const getFieldBrowserCategoryTitleClassName = ({ - categoryId, - timelineId, -}: { - categoryId: string; - timelineId: string; -}): string => `field-browser-category-title-${categoryId}-${timelineId}`; - -/** Returns the class name for a field browser search input */ -export const getFieldBrowserSearchInputClassName = (timelineId: string): string => - `field-browser-search-input-${timelineId}`; /** Returns true if the specified category has at least one field */ export const categoryHasFields = (category: Partial): boolean => @@ -160,272 +119,22 @@ export const getAlertColumnHeader = (timelineId: string, fieldId: string) => ? defaultHeaders.find((c) => c.id === fieldId) ?? {} : {}; -export const CATEGORIES_PANE_CLASS_NAME = 'categories-pane'; export const CATEGORY_TABLE_CLASS_NAME = 'category-table'; export const CLOSE_BUTTON_CLASS_NAME = 'close-button'; export const RESET_FIELDS_CLASS_NAME = 'reset-fields'; -export const VIEW_ALL_BUTTON_CLASS_NAME = 'view-all'; - -export const categoriesPaneHasFocus = (containerElement: HTMLElement | null): boolean => - elementOrChildrenHasFocus( - containerElement?.querySelector(`.${CATEGORIES_PANE_CLASS_NAME}`) - ); - -export const categoryTableHasFocus = (containerElement: HTMLElement | null): boolean => - elementOrChildrenHasFocus( - containerElement?.querySelector(`.${CATEGORY_TABLE_CLASS_NAME}`) - ); - -export const closeButtonHasFocus = (containerElement: HTMLElement | null): boolean => - elementOrChildrenHasFocus( - containerElement?.querySelector(`.${CLOSE_BUTTON_CLASS_NAME}`) - ); - -export const searchInputHasFocus = ({ - containerElement, - timelineId, -}: { - containerElement: HTMLElement | null; - timelineId: string; -}): boolean => - elementOrChildrenHasFocus( - containerElement?.querySelector( - `.${getFieldBrowserSearchInputClassName(timelineId)}` - ) - ); - -export const viewAllHasFocus = (containerElement: HTMLElement | null): boolean => - elementOrChildrenHasFocus( - containerElement?.querySelector(`.${VIEW_ALL_BUTTON_CLASS_NAME}`) - ); - -export const resetButtonHasFocus = (containerElement: HTMLElement | null): boolean => - elementOrChildrenHasFocus( - containerElement?.querySelector(`.${RESET_FIELDS_CLASS_NAME}`) - ); - -export const scrollCategoriesPane = ({ - containerElement, - selectedCategoryId, - timelineId, -}: { - containerElement: HTMLElement | null; - selectedCategoryId: string; - timelineId: string; -}) => { - if (selectedCategoryId !== '') { - const selectedCategories = - containerElement?.getElementsByClassName( - getCategoryPaneCategoryClassName({ - categoryId: selectedCategoryId, - timelineId, - }) - ) ?? []; - - if (selectedCategories.length > 0) { - selectedCategories[0].scrollIntoView(); - } - } -}; - -export const focusCategoriesPane = ({ - containerElement, - selectedCategoryId, - timelineId, -}: { - containerElement: HTMLElement | null; - selectedCategoryId: string; - timelineId: string; -}) => { - if (selectedCategoryId !== '') { - const selectedCategories = - containerElement?.getElementsByClassName( - getCategoryPaneCategoryClassName({ - categoryId: selectedCategoryId, - timelineId, - }) - ) ?? []; - - if (selectedCategories.length > 0) { - (selectedCategories[0] as HTMLButtonElement).focus(); - } - } -}; - -export const focusCategoryTable = (containerElement: HTMLElement | null) => { - const firstEntry = containerElement?.querySelector( - `.${CATEGORY_TABLE_CLASS_NAME} [data-colindex="1"]` - ); - - if (firstEntry != null) { - firstEntry.focus(); - } else { - skipFocusInContainerTo({ - containerElement, - className: CATEGORY_TABLE_CLASS_NAME, - }); - } -}; - -export const focusCloseButton = (containerElement: HTMLElement | null) => - skipFocusInContainerTo({ - containerElement, - className: CLOSE_BUTTON_CLASS_NAME, - }); - -export const focusResetFieldsButton = (containerElement: HTMLElement | null) => - skipFocusInContainerTo({ containerElement, className: RESET_FIELDS_CLASS_NAME }); - -export const focusSearchInput = ({ - containerElement, - timelineId, -}: { - containerElement: HTMLElement | null; - timelineId: string; -}) => - skipFocusInContainerTo({ - containerElement, - className: getFieldBrowserSearchInputClassName(timelineId), - }); - -export const focusViewAllButton = (containerElement: HTMLElement | null) => - skipFocusInContainerTo({ containerElement, className: VIEW_ALL_BUTTON_CLASS_NAME }); - -export const onCategoriesPaneFocusChanging = ({ - containerElement, - shiftKey, - timelineId, -}: { - containerElement: HTMLElement | null; - shiftKey: boolean; - timelineId: string; -}) => - shiftKey - ? focusSearchInput({ - containerElement, - timelineId, - }) - : focusViewAllButton(containerElement); - -export const onCategoryTableFocusChanging = ({ - containerElement, - shiftKey, -}: { - containerElement: HTMLElement | null; - shiftKey: boolean; -}) => (shiftKey ? focusViewAllButton(containerElement) : focusResetFieldsButton(containerElement)); - -export const onCloseButtonFocusChanging = ({ - containerElement, - shiftKey, - timelineId, -}: { - containerElement: HTMLElement | null; - shiftKey: boolean; - timelineId: string; -}) => - shiftKey - ? focusResetFieldsButton(containerElement) - : focusSearchInput({ containerElement, timelineId }); - -export const onSearchInputFocusChanging = ({ - containerElement, - selectedCategoryId, - shiftKey, - timelineId, -}: { - containerElement: HTMLElement | null; - selectedCategoryId: string; - shiftKey: boolean; - timelineId: string; -}) => - shiftKey - ? focusCloseButton(containerElement) - : focusCategoriesPane({ containerElement, selectedCategoryId, timelineId }); - -export const onViewAllFocusChanging = ({ - containerElement, - selectedCategoryId, - shiftKey, - timelineId, -}: { - containerElement: HTMLElement | null; - selectedCategoryId: string; - shiftKey: boolean; - timelineId: string; -}) => - shiftKey - ? focusCategoriesPane({ containerElement, selectedCategoryId, timelineId }) - : focusCategoryTable(containerElement); - -export const onResetButtonFocusChanging = ({ - containerElement, - shiftKey, -}: { - containerElement: HTMLElement | null; - shiftKey: boolean; -}) => (shiftKey ? focusCategoryTable(containerElement) : focusCloseButton(containerElement)); - -export const onFieldsBrowserTabPressed = ({ - containerElement, - keyboardEvent, - selectedCategoryId, - timelineId, -}: { - containerElement: HTMLElement | null; - keyboardEvent: React.KeyboardEvent; - selectedCategoryId: string; - timelineId: string; -}) => { - const { shiftKey } = keyboardEvent; - - if (searchInputHasFocus({ containerElement, timelineId })) { - stopPropagationAndPreventDefault(keyboardEvent); - onSearchInputFocusChanging({ - containerElement, - selectedCategoryId, - shiftKey, - timelineId, - }); - } else if (categoriesPaneHasFocus(containerElement)) { - stopPropagationAndPreventDefault(keyboardEvent); - onCategoriesPaneFocusChanging({ - containerElement, - shiftKey, - timelineId, - }); - } else if (viewAllHasFocus(containerElement)) { - stopPropagationAndPreventDefault(keyboardEvent); - onViewAllFocusChanging({ - containerElement, - selectedCategoryId, - shiftKey, - timelineId, - }); - } else if (categoryTableHasFocus(containerElement)) { - stopPropagationAndPreventDefault(keyboardEvent); - onCategoryTableFocusChanging({ - containerElement, - shiftKey, - }); - } else if (resetButtonHasFocus(containerElement)) { - stopPropagationAndPreventDefault(keyboardEvent); - onResetButtonFocusChanging({ - containerElement, - shiftKey, - }); - } else if (closeButtonHasFocus(containerElement)) { - stopPropagationAndPreventDefault(keyboardEvent); - onCloseButtonFocusChanging({ - containerElement, - shiftKey, - timelineId, - }); - } -}; export const CountBadge = styled(EuiBadge)` margin-left: 5px; ` as unknown as typeof EuiBadge; CountBadge.displayName = 'CountBadge'; + +export const CategoryName = styled.span<{ bold: boolean }>` + font-weight: ${({ bold }) => (bold ? 'bold' : 'normal')}; +`; +CategoryName.displayName = 'CategoryName'; + +export const CategorySelectableContainer = styled.div` + width: 300px; +`; +CategorySelectableContainer.displayName = 'CategorySelectableContainer'; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.test.tsx index b8bc2a12ffd6e..7db742fd11302 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.test.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import { mount } from 'enzyme'; import React from 'react'; -import { waitFor } from '@testing-library/react'; +import { act, fireEvent, render, waitFor } from '@testing-library/react'; import { mockBrowserFields, TestProviders } from '../../../../mock'; @@ -18,12 +17,8 @@ import { StatefulFieldsBrowserComponent } from '.'; describe('StatefulFieldsBrowser', () => { const timelineId = 'test'; - beforeEach(() => { - window.HTMLElement.prototype.scrollIntoView = jest.fn(); - }); - - test('it renders the Fields button, which displays the fields browser on click', () => { - const wrapper = mount( + it('should render the Fields button, which displays the fields browser on click', () => { + const result = render( { ); - expect(wrapper.find('[data-test-subj="show-field-browser"]').exists()).toBe(true); + expect(result.getByTestId('show-field-browser')).toBeInTheDocument(); }); describe('toggleShow', () => { - test('it does NOT render the fields browser until the Fields button is clicked', () => { - const wrapper = mount( + it('should NOT render the fields browser until the Fields button is clicked', () => { + const result = render( { ); - expect(wrapper.find('[data-test-subj="fields-browser-container"]').exists()).toBe(false); + expect(result.queryByTestId('fields-browser-container')).toBeNull(); }); - test('it renders the fields browser when the Fields button is clicked', () => { - const wrapper = mount( + it('should render the fields browser when the Fields button is clicked', async () => { + const result = render( { /> ); - - wrapper.find('[data-test-subj="show-field-browser"]').first().simulate('click'); - - expect(wrapper.find('[data-test-subj="fields-browser-container"]').exists()).toBe(true); + result.getByTestId('show-field-browser').click(); + await waitFor(() => { + expect(result.getByTestId('fields-browser-container')).toBeInTheDocument(); + }); }); }); - describe('updateSelectedCategoryId', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - test('it updates the selectedCategoryId state, which makes the category bold, when the user clicks a category name in the left hand side of the field browser', async () => { - const wrapper = mount( + describe('updateSelectedCategoryIds', () => { + it('should add a selected category, which creates the category badge', async () => { + const result = render( { ); - wrapper.find('[data-test-subj="show-field-browser"]').first().simulate('click'); + result.getByTestId('show-field-browser').click(); + await waitFor(() => { + expect(result.getByTestId('fields-browser-container')).toBeInTheDocument(); + }); + + await act(async () => { + result.getByTestId('categories-filter-button').click(); + }); + await act(async () => { + result.getByTestId('categories-selector-option-base').click(); + }); + + expect(result.getByTestId('category-badge-base')).toBeInTheDocument(); + }); + + it('should remove a selected category, which deletes the category badge', async () => { + const result = render( + + + + ); - wrapper.find(`.field-browser-category-pane-auditd-${timelineId}`).first().simulate('click'); + result.getByTestId('show-field-browser').click(); await waitFor(() => { - wrapper.update(); - expect( - wrapper - .find(`.field-browser-category-pane-auditd-${timelineId}`) - .find('[data-test-subj="categoryName"]') - .at(1) - ).toHaveStyleRule('font-weight', 'bold', { modifier: '.euiText' }); + expect(result.getByTestId('fields-browser-container')).toBeInTheDocument(); }); + + await act(async () => { + result.getByTestId('categories-filter-button').click(); + }); + await act(async () => { + result.getByTestId('categories-selector-option-base').click(); + }); + expect(result.getByTestId('category-badge-base')).toBeInTheDocument(); + + await act(async () => { + result.getByTestId('category-badge-unselect-base').click(); + }); + expect(result.queryByTestId('category-badge-base')).toBeNull(); }); - test('it updates the selectedCategoryId state according to most fields returned', async () => { - const wrapper = mount( + it('should update the available categories according to the search input', async () => { + const result = render( { ); + result.getByTestId('show-field-browser').click(); await waitFor(() => { - wrapper.find('[data-test-subj="show-field-browser"]').first().simulate('click'); - jest.runOnlyPendingTimers(); - wrapper.update(); - - expect( - wrapper - .find(`.field-browser-category-pane-cloud-${timelineId}`) - .find('[data-test-subj="categoryName"]') - .at(1) - ).toHaveStyleRule('font-weight', 'normal', { modifier: '.euiText' }); + expect(result.getByTestId('fields-browser-container')).toBeInTheDocument(); }); + result.getByTestId('categories-filter-button').click(); + expect(result.getByTestId('categories-selector-option-base')).toBeInTheDocument(); + + fireEvent.change(result.getByTestId('field-search'), { target: { value: 'client' } }); await waitFor(() => { - wrapper - .find('[data-test-subj="field-search"]') - .last() - .simulate('change', { target: { value: 'cloud' } }); - - jest.runOnlyPendingTimers(); - wrapper.update(); - expect( - wrapper - .find(`.field-browser-category-pane-cloud-${timelineId}`) - .find('[data-test-subj="categoryName"]') - .at(1) - ).toHaveStyleRule('font-weight', 'bold', { modifier: '.euiText' }); + expect(result.queryByTestId('categories-selector-option-base')).toBeNull(); }); + expect(result.queryByTestId('categories-selector-option-client')).toBeInTheDocument(); }); }); - test('it renders the Fields Browser button as a settings gear when the isEventViewer prop is true', () => { + it('should render the Fields Browser button as a settings gear when the isEventViewer prop is true', () => { const isEventViewer = true; - const wrapper = mount( + const result = render( { ); - expect(wrapper.find('[data-test-subj="show-field-browser"]').first().exists()).toBe(true); + expect(result.getByTestId('show-field-browser')).toBeInTheDocument(); }); - test('it renders the Fields Browser button as a settings gear when the isEventViewer prop is false', () => { - const isEventViewer = true; + it('should render the Fields Browser button as a settings gear when the isEventViewer prop is false', () => { + const isEventViewer = false; - const wrapper = mount( + const result = render( { ); - expect(wrapper.find('[data-test-subj="show-field-browser"]').first().exists()).toBe(true); + expect(result.getByTestId('show-field-browser')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.tsx index 13549e2d5be10..c5647c973b9d8 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.tsx @@ -6,15 +6,15 @@ */ import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; +import { debounce } from 'lodash'; import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react'; import styled from 'styled-components'; import type { BrowserFields } from '../../../../../common/search_strategy/index_fields'; -import { DEFAULT_CATEGORY_NAME } from '../../body/column_headers/default_headers'; +import type { FieldBrowserProps } from '../../../../../common/types/fields_browser'; import { FieldsBrowser } from './field_browser'; import { filterBrowserFieldsByFieldName, mergeBrowserFieldsWithDefaultCategory } from './helpers'; import * as i18n from './translations'; -import type { FieldBrowserProps } from './types'; const FIELDS_BUTTON_CLASS_NAME = 'fields-button'; @@ -34,26 +34,48 @@ export const StatefulFieldsBrowserComponent: React.FC = ({ timelineId, columnHeaders, browserFields, - createFieldComponent, + options, width, }) => { const customizeColumnsButtonRef = useRef(null); - /** tracks the latest timeout id from `setTimeout`*/ - const inputTimeoutId = useRef(0); - /** all field names shown in the field browser must contain this string (when specified) */ const [filterInput, setFilterInput] = useState(''); - + /** debounced filterInput, the one that is applied to the filteredBrowserFields */ const [appliedFilterInput, setAppliedFilterInput] = useState(''); /** all fields in this collection have field names that match the filterInput */ const [filteredBrowserFields, setFilteredBrowserFields] = useState(null); /** when true, show a spinner in the input to indicate the field browser is searching for matching field names */ const [isSearching, setIsSearching] = useState(false); /** this category will be displayed in the right-hand pane of the field browser */ - const [selectedCategoryId, setSelectedCategoryId] = useState(DEFAULT_CATEGORY_NAME); + const [selectedCategoryIds, setSelectedCategoryIds] = useState([]); /** show the field browser */ const [show, setShow] = useState(false); + // debounced function to apply the input filter + // will delay the call to setAppliedFilterInput by INPUT_TIMEOUT ms + // the parameter used will be the last one passed + const debouncedApplyFilterInput = useMemo( + () => + debounce((input: string) => { + setAppliedFilterInput(input); + }, INPUT_TIMEOUT), + [] + ); + useEffect(() => { + return () => { + debouncedApplyFilterInput.cancel(); + }; + }, [debouncedApplyFilterInput]); + + useEffect(() => { + const newFilteredBrowserFields = filterBrowserFieldsByFieldName({ + browserFields: mergeBrowserFieldsWithDefaultCategory(browserFields), + substring: appliedFilterInput, + }); + setFilteredBrowserFields(newFilteredBrowserFields); + setIsSearching(false); + }, [appliedFilterInput, browserFields]); + /** Shows / hides the field browser */ const onShow = useCallback(() => { setShow(true); @@ -65,65 +87,19 @@ export const StatefulFieldsBrowserComponent: React.FC = ({ setAppliedFilterInput(''); setFilteredBrowserFields(null); setIsSearching(false); - setSelectedCategoryId(DEFAULT_CATEGORY_NAME); + setSelectedCategoryIds([]); setShow(false); }, []); - const newFilteredBrowserFields = useMemo(() => { - return filterBrowserFieldsByFieldName({ - browserFields: mergeBrowserFieldsWithDefaultCategory(browserFields), - substring: appliedFilterInput, - }); - }, [appliedFilterInput, browserFields]); - - const newSelectedCategoryId = useMemo(() => { - if (appliedFilterInput === '' || Object.keys(newFilteredBrowserFields).length === 0) { - return DEFAULT_CATEGORY_NAME; - } else { - return Object.keys(newFilteredBrowserFields) - .sort() - .reduce((selected, category) => { - const filteredBrowserFieldsByCategory = - (newFilteredBrowserFields[category] && newFilteredBrowserFields[category].fields) || []; - const filteredBrowserFieldsBySelected = - (newFilteredBrowserFields[selected] && newFilteredBrowserFields[selected].fields) || []; - return newFilteredBrowserFields[category].fields != null && - newFilteredBrowserFields[selected].fields != null && - Object.keys(filteredBrowserFieldsByCategory).length > - Object.keys(filteredBrowserFieldsBySelected).length - ? category - : selected; - }, Object.keys(newFilteredBrowserFields)[0]); - } - }, [appliedFilterInput, newFilteredBrowserFields]); - /** Invoked when the user types in the filter input */ - const updateFilter = useCallback((newFilterInput: string) => { - setFilterInput(newFilterInput); - setIsSearching(true); - }, []); - - useEffect(() => { - if (inputTimeoutId.current !== 0) { - clearTimeout(inputTimeoutId.current); // ⚠️ mutation: cancel any previous timers - } - // ⚠️ mutation: schedule a new timer that will apply the filter when it fires: - inputTimeoutId.current = window.setTimeout(() => { - setIsSearching(false); - setAppliedFilterInput(filterInput); - }, INPUT_TIMEOUT); - return () => { - clearTimeout(inputTimeoutId.current); - }; - }, [filterInput]); - - useEffect(() => { - setFilteredBrowserFields(newFilteredBrowserFields); - }, [newFilteredBrowserFields]); - - useEffect(() => { - setSelectedCategoryId(newSelectedCategoryId); - }, [newSelectedCategoryId]); + const updateFilter = useCallback( + (newFilterInput: string) => { + setIsSearching(true); + setFilterInput(newFilterInput); + debouncedApplyFilterInput(newFilterInput); + }, + [debouncedApplyFilterInput] + ); // only merge in the default category if the field browser is visible const browserFieldsWithDefaultCategory = useMemo(() => { @@ -150,19 +126,19 @@ export const StatefulFieldsBrowserComponent: React.FC = ({ {show && ( diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/search.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/search.test.tsx index f5668b1bdc08d..fb6363e244459 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/search.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/search.test.tsx @@ -7,7 +7,7 @@ import { mount } from 'enzyme'; import React from 'react'; -import { mockBrowserFields, TestProviders } from '../../../../mock'; +import { TestProviders } from '../../../../mock'; import { Search } from './search'; const timelineId = 'test'; @@ -17,7 +17,6 @@ describe('Search', () => { const wrapper = mount( { const wrapper = mount( { const wrapper = mount( { const wrapper = mount( { expect(onSearchInputChange).toBeCalled(); }); - - test('it returns the expected categories count when filteredBrowserFields is empty', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="categories-count"]').first().text()).toEqual( - '0 categories' - ); - }); - - test('it returns the expected categories count when filteredBrowserFields is NOT empty', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="categories-count"]').first().text()).toEqual( - '12 categories' - ); - }); - - test('it returns the expected fields count when filteredBrowserFields is empty', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="fields-count"]').first().text()).toEqual('0 fields'); - }); - - test('it returns the expected fields count when filteredBrowserFields is NOT empty', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="fields-count"]').first().text()).toEqual('34 fields'); - }); }); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/search.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/search.tsx index 935952fbf37e0..037dcdc9033d2 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/search.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/search.tsx @@ -6,75 +6,28 @@ */ import React from 'react'; -import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import styled from 'styled-components'; -import type { BrowserFields } from '../../../../../common/search_strategy'; - -import { getFieldBrowserSearchInputClassName, getFieldCount } from './helpers'; - +import { EuiFieldSearch } from '@elastic/eui'; import * as i18n from './translations'; - -const CountsFlexGroup = styled(EuiFlexGroup)` - margin-top: ${({ theme }) => theme.eui.euiSizeXS}; - margin-left: ${({ theme }) => theme.eui.euiSizeXS}; -`; - -CountsFlexGroup.displayName = 'CountsFlexGroup'; - interface Props { - filteredBrowserFields: BrowserFields; isSearching: boolean; onSearchInputChange: (event: React.ChangeEvent) => void; searchInput: string; timelineId: string; } -const CountRow = React.memo>(({ filteredBrowserFields }) => ( - - - - {i18n.CATEGORIES_COUNT(Object.keys(filteredBrowserFields).length)} - - - - - - {i18n.FIELDS_COUNT( - Object.keys(filteredBrowserFields).reduce( - (fieldsCount, category) => getFieldCount(filteredBrowserFields[category]) + fieldsCount, - 0 - ) - )} - - - -)); - -CountRow.displayName = 'CountRow'; - const inputRef = (node: HTMLInputElement | null) => node?.focus(); export const Search = React.memo( - ({ isSearching, filteredBrowserFields, onSearchInputChange, searchInput, timelineId }) => ( - <> - - - + ({ isSearching, onSearchInputChange, searchInput, timelineId }) => ( + ) ); - Search.displayName = 'Search'; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/translations.ts b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/translations.ts index ac0160fad6cde..eab412971c580 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/translations.ts +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/translations.ts @@ -21,21 +21,6 @@ export const CATEGORIES_COUNT = (totalCount: number) => defaultMessage: '{totalCount} {totalCount, plural, =1 {category} other {categories}}', }); -export const CATEGORY_LINK = ({ category, totalCount }: { category: string; totalCount: number }) => - i18n.translate('xpack.timelines.fieldBrowser.categoryLinkAriaLabel', { - values: { category, totalCount }, - defaultMessage: - '{category} {totalCount} {totalCount, plural, =1 {field} other {fields}}. Click this button to select the {category} category.', - }); - -export const CATEGORY_FIELDS_TABLE_CAPTION = (categoryId: string) => - i18n.translate('xpack.timelines.fieldBrowser.categoryFieldsTableCaption', { - defaultMessage: 'category {categoryId} fields', - values: { - categoryId, - }, - }); - export const CLOSE = i18n.translate('xpack.timelines.fieldBrowser.closeButton', { defaultMessage: 'Close', }); @@ -56,6 +41,10 @@ export const DESCRIPTION_FOR_FIELD = (field: string) => defaultMessage: 'Description for field {field}:', }); +export const NAME = i18n.translate('xpack.timelines.fieldBrowser.fieldName', { + defaultMessage: 'Name', +}); + export const FIELD = i18n.translate('xpack.timelines.fieldBrowser.fieldLabel', { defaultMessage: 'Field', }); @@ -64,10 +53,14 @@ export const FIELDS = i18n.translate('xpack.timelines.fieldBrowser.fieldsTitle', defaultMessage: 'Fields', }); +export const FIELDS_SHOWING = i18n.translate('xpack.timelines.fieldBrowser.fieldsCountShowing', { + defaultMessage: 'Showing', +}); + export const FIELDS_COUNT = (totalCount: number) => i18n.translate('xpack.timelines.fieldBrowser.fieldsCountTitle', { values: { totalCount }, - defaultMessage: '{totalCount} {totalCount, plural, =1 {field} other {fields}}', + defaultMessage: '{totalCount, plural, =1 {field} other {fields}}', }); export const FILTER_PLACEHOLDER = i18n.translate('xpack.timelines.fieldBrowser.filterPlaceholder', { @@ -90,14 +83,6 @@ export const RESET_FIELDS = i18n.translate('xpack.timelines.fieldBrowser.resetFi defaultMessage: 'Reset Fields', }); -export const VIEW_ALL_CATEGORY_FIELDS = (categoryId: string) => - i18n.translate('xpack.timelines.fieldBrowser.viewCategoryTooltip', { - defaultMessage: 'View all {categoryId} fields', - values: { - categoryId, - }, - }); - export const VIEW_COLUMN = (field: string) => i18n.translate('xpack.timelines.fieldBrowser.viewColumnCheckboxAriaLabel', { values: { field }, diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/types.ts b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/types.ts deleted file mode 100644 index bcf7287950624..0000000000000 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/types.ts +++ /dev/null @@ -1,27 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CreateFieldComponentType } from '../../../../../common/types'; -import type { BrowserFields } from '../../../../../common/search_strategy/index_fields'; -import type { ColumnHeaderOptions } from '../../../../../common/types/timeline/columns'; - -export type OnFieldSelected = (fieldId: string) => void; - -export interface FieldBrowserProps { - /** The timeline associated with this field browser */ - timelineId: string; - /** The timeline's current column headers */ - columnHeaders: ColumnHeaderOptions[]; - /** A map of categoryId -> metadata about the fields in that category */ - browserFields: BrowserFields; - - createFieldComponent?: CreateFieldComponentType; - /** When true, this Fields Browser is being used as an "events viewer" */ - isEventViewer?: boolean; - /** The width of the field browser */ - width?: number; -} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 70d65550a4056..c35b5dbe66678 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -27035,9 +27035,7 @@ "xpack.timelines.exitFullScreenButton": "全画面を終了", "xpack.timelines.fieldBrowser.categoriesCountTitle": "{totalCount} {totalCount, plural, other {カテゴリ}}", "xpack.timelines.fieldBrowser.categoriesTitle": "カテゴリー", - "xpack.timelines.fieldBrowser.categoryFieldsTableCaption": "カテゴリ {categoryId} フィールド", "xpack.timelines.fieldBrowser.categoryLabel": "カテゴリー", - "xpack.timelines.fieldBrowser.categoryLinkAriaLabel": "{category} {totalCount} {totalCount, plural, other {フィールド}}このボタンをクリックすると、{category} カテゴリを選択します。", "xpack.timelines.fieldBrowser.closeButton": "閉じる", "xpack.timelines.fieldBrowser.descriptionForScreenReaderOnly": "フィールド {field} の説明:", "xpack.timelines.fieldBrowser.descriptionLabel": "説明", @@ -27049,7 +27047,6 @@ "xpack.timelines.fieldBrowser.noFieldsMatchInputLabel": "{searchInput} に一致するフィールドがありません", "xpack.timelines.fieldBrowser.noFieldsMatchLabel": "一致するフィールドがありません", "xpack.timelines.fieldBrowser.resetFieldsLink": "フィールドをリセット", - "xpack.timelines.fieldBrowser.viewCategoryTooltip": "すべての {categoryId} フィールドを表示します", "xpack.timelines.fieldBrowser.viewColumnCheckboxAriaLabel": "{field} 列を表示", "xpack.timelines.footer.autoRefreshActiveDescription": "自動更新アクション", "xpack.timelines.footer.autoRefreshActiveTooltip": "自動更新が有効な間、タイムラインはクエリに一致する最新の {numberOfItems} 件のイベントを表示します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 90eb0d3c35f65..f706762740ad8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -27067,9 +27067,7 @@ "xpack.timelines.exitFullScreenButton": "退出全屏", "xpack.timelines.fieldBrowser.categoriesCountTitle": "{totalCount} {totalCount, plural, other {个类别}}", "xpack.timelines.fieldBrowser.categoriesTitle": "类别", - "xpack.timelines.fieldBrowser.categoryFieldsTableCaption": "类别 {categoryId} 字段", "xpack.timelines.fieldBrowser.categoryLabel": "类别", - "xpack.timelines.fieldBrowser.categoryLinkAriaLabel": "{category} {totalCount} 个{totalCount, plural, other {字段}}。单击此按钮可选择 {category} 类别。", "xpack.timelines.fieldBrowser.closeButton": "关闭", "xpack.timelines.fieldBrowser.descriptionForScreenReaderOnly": "{field} 字段的描述:", "xpack.timelines.fieldBrowser.descriptionLabel": "描述", @@ -27081,7 +27079,6 @@ "xpack.timelines.fieldBrowser.noFieldsMatchInputLabel": "没有字段匹配“{searchInput}”", "xpack.timelines.fieldBrowser.noFieldsMatchLabel": "没有字段匹配", "xpack.timelines.fieldBrowser.resetFieldsLink": "重置字段", - "xpack.timelines.fieldBrowser.viewCategoryTooltip": "查看所有 {categoryId} 字段", "xpack.timelines.fieldBrowser.viewColumnCheckboxAriaLabel": "查看 {field} 列", "xpack.timelines.footer.autoRefreshActiveDescription": "自动刷新已启用", "xpack.timelines.footer.autoRefreshActiveTooltip": "自动刷新已启用时,时间线将显示匹配查询的最近 {numberOfItems} 个事件。", From eca203ce73500e5ff336656e00a8eb658ec7af20 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Mon, 7 Mar 2022 16:10:41 +0100 Subject: [PATCH 020/140] [AggConfigs] Add TopMetrics agg (#125936) --- packages/kbn-doc-links/src/get_doc_links.ts | 1 + .../data/common/search/aggs/agg_types.ts | 2 + .../common/search/aggs/aggs_service.test.ts | 2 + .../data/common/search/aggs/metrics/index.ts | 2 + .../metrics/lib/parent_pipeline_agg_helper.ts | 1 + .../lib/sibling_pipeline_agg_helper.ts | 1 + .../search/aggs/metrics/metric_agg_type.ts | 1 + .../search/aggs/metrics/metric_agg_types.ts | 1 + .../search/aggs/metrics/top_metrics.test.ts | 194 ++++++++++++++++++ .../common/search/aggs/metrics/top_metrics.ts | 155 ++++++++++++++ .../aggs/metrics/top_metrics_fn.test.ts | 79 +++++++ .../search/aggs/metrics/top_metrics_fn.ts | 106 ++++++++++ .../common/search/aggs/param_types/field.ts | 1 + src/plugins/data/common/search/aggs/types.ts | 4 + .../public/search/aggs/aggs_service.test.ts | 4 +- .../public/components/agg_params_map.ts | 5 + .../public/components/controls/metric_agg.tsx | 9 +- .../run_pipeline/esaggs_topmetrics.ts | 112 ++++++++++ .../test_suites/run_pipeline/index.ts | 2 + 19 files changed, 679 insertions(+), 3 deletions(-) create mode 100644 src/plugins/data/common/search/aggs/metrics/top_metrics.test.ts create mode 100644 src/plugins/data/common/search/aggs/metrics/top_metrics.ts create mode 100644 src/plugins/data/common/search/aggs/metrics/top_metrics_fn.test.ts create mode 100644 src/plugins/data/common/search/aggs/metrics/top_metrics_fn.ts create mode 100644 test/interpreter_functional/test_suites/run_pipeline/esaggs_topmetrics.ts diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index d73760b280d49..03948af637910 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -196,6 +196,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { std_dev: `${ELASTICSEARCH_DOCS}search-aggregations-metrics-extendedstats-aggregation.html`, sum: `${ELASTICSEARCH_DOCS}search-aggregations-metrics-sum-aggregation.html`, top_hits: `${ELASTICSEARCH_DOCS}search-aggregations-metrics-top-hits-aggregation.html`, + top_metrics: `${ELASTICSEARCH_DOCS}search-aggregations-metrics-top-metrics.html`, }, runtimeFields: { overview: `${ELASTICSEARCH_DOCS}runtime.html`, diff --git a/src/plugins/data/common/search/aggs/agg_types.ts b/src/plugins/data/common/search/aggs/agg_types.ts index 01ccd401c07ac..d7750c48016cd 100644 --- a/src/plugins/data/common/search/aggs/agg_types.ts +++ b/src/plugins/data/common/search/aggs/agg_types.ts @@ -36,6 +36,7 @@ export const getAggTypes = () => ({ { name: METRIC_TYPES.PERCENTILES, fn: metrics.getPercentilesMetricAgg }, { name: METRIC_TYPES.PERCENTILE_RANKS, fn: metrics.getPercentileRanksMetricAgg }, { name: METRIC_TYPES.TOP_HITS, fn: metrics.getTopHitMetricAgg }, + { name: METRIC_TYPES.TOP_METRICS, fn: metrics.getTopMetricsMetricAgg }, { name: METRIC_TYPES.DERIVATIVE, fn: metrics.getDerivativeMetricAgg }, { name: METRIC_TYPES.CUMULATIVE_SUM, fn: metrics.getCumulativeSumMetricAgg }, { name: METRIC_TYPES.MOVING_FN, fn: metrics.getMovingAvgMetricAgg }, @@ -109,4 +110,5 @@ export const getAggTypesFunctions = () => [ metrics.aggStdDeviation, metrics.aggSum, metrics.aggTopHit, + metrics.aggTopMetrics, ]; diff --git a/src/plugins/data/common/search/aggs/aggs_service.test.ts b/src/plugins/data/common/search/aggs/aggs_service.test.ts index b7237c7b80134..6090e965489e7 100644 --- a/src/plugins/data/common/search/aggs/aggs_service.test.ts +++ b/src/plugins/data/common/search/aggs/aggs_service.test.ts @@ -95,6 +95,7 @@ describe('Aggs service', () => { "percentiles", "percentile_ranks", "top_hits", + "top_metrics", "derivative", "cumulative_sum", "moving_avg", @@ -147,6 +148,7 @@ describe('Aggs service', () => { "percentiles", "percentile_ranks", "top_hits", + "top_metrics", "derivative", "cumulative_sum", "moving_avg", diff --git a/src/plugins/data/common/search/aggs/metrics/index.ts b/src/plugins/data/common/search/aggs/metrics/index.ts index d37b74a1a28ae..4d80e36325100 100644 --- a/src/plugins/data/common/search/aggs/metrics/index.ts +++ b/src/plugins/data/common/search/aggs/metrics/index.ts @@ -56,3 +56,5 @@ export * from './sum_fn'; export * from './sum'; export * from './top_hit_fn'; export * from './top_hit'; +export * from './top_metrics'; +export * from './top_metrics_fn'; diff --git a/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts b/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts index 478b8309272e3..1fe703313218d 100644 --- a/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts +++ b/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts @@ -15,6 +15,7 @@ import { parentPipelineAggWriter } from './parent_pipeline_agg_writer'; const metricAggFilter = [ '!top_hits', + '!top_metrics', '!percentiles', '!percentile_ranks', '!median', diff --git a/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts index f8c903b8cfe42..243a119847a2c 100644 --- a/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts +++ b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts @@ -13,6 +13,7 @@ import { IMetricAggConfig, MetricAggParam } from '../metric_agg_type'; const metricAggFilter: string[] = [ '!top_hits', + '!top_metrics', '!percentiles', '!percentile_ranks', '!median', diff --git a/src/plugins/data/common/search/aggs/metrics/metric_agg_type.ts b/src/plugins/data/common/search/aggs/metrics/metric_agg_type.ts index 6ddb0fdd9410d..5237c1ecffe58 100644 --- a/src/plugins/data/common/search/aggs/metrics/metric_agg_type.ts +++ b/src/plugins/data/common/search/aggs/metrics/metric_agg_type.ts @@ -22,6 +22,7 @@ export interface MetricAggParam extends AggParamType { filterFieldTypes?: FieldTypes; onlyAggregatable?: boolean; + scriptable?: boolean; } const metricType = 'metrics'; diff --git a/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts b/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts index a308153b3816b..eed6d0a378fc2 100644 --- a/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts +++ b/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts @@ -27,6 +27,7 @@ export enum METRIC_TYPES { SERIAL_DIFF = 'serial_diff', SUM = 'sum', TOP_HITS = 'top_hits', + TOP_METRICS = 'top_metrics', PERCENTILES = 'percentiles', PERCENTILE_RANKS = 'percentile_ranks', STD_DEV = 'std_dev', diff --git a/src/plugins/data/common/search/aggs/metrics/top_metrics.test.ts b/src/plugins/data/common/search/aggs/metrics/top_metrics.test.ts new file mode 100644 index 0000000000000..9bf5f581aa0a4 --- /dev/null +++ b/src/plugins/data/common/search/aggs/metrics/top_metrics.test.ts @@ -0,0 +1,194 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getTopMetricsMetricAgg } from './top_metrics'; +import { AggConfigs } from '../agg_configs'; +import { mockAggTypesRegistry } from '../test_helpers'; +import { IMetricAggConfig } from './metric_agg_type'; +import { KBN_FIELD_TYPES } from '../../../../common'; + +describe('Top metrics metric', () => { + let aggConfig: IMetricAggConfig; + + const init = ({ + fieldName = 'field', + fieldType = KBN_FIELD_TYPES.NUMBER, + sortFieldName = 'sortField', + sortFieldType = KBN_FIELD_TYPES.NUMBER, + sortOrder = 'desc', + size = 1, + }: any) => { + const typesRegistry = mockAggTypesRegistry(); + const field = { + name: fieldName, + displayName: fieldName, + type: fieldType, + }; + + const sortField = { + name: sortFieldName, + displayName: sortFieldName, + type: sortFieldType, + }; + + const params = { + size, + field: field.name, + sortField: sortField.name, + sortOrder: { + value: sortOrder, + }, + }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: (name: string) => { + if (name === sortFieldName) return sortField; + if (name === fieldName) return field; + return null; + }, + filter: () => [field, sortField], + }, + } as any; + + const aggConfigs = new AggConfigs( + indexPattern, + [ + { + id: '1', + type: 'top_metrics', + schema: 'metric', + params, + }, + ], + { typesRegistry } + ); + + // Grab the aggConfig off the vis (we don't actually use the vis for anything else) + aggConfig = aggConfigs.aggs[0] as IMetricAggConfig; + }; + + it('should return a label prefixed with Last if sorting in descending order', () => { + init({ fieldName: 'bytes', sortFieldName: '@timestamp' }); + expect(getTopMetricsMetricAgg().makeLabel(aggConfig)).toEqual( + 'Last "bytes" value by "@timestamp"' + ); + }); + + it('should return a label prefixed with First if sorting in ascending order', () => { + init({ + fieldName: 'bytes', + sortFieldName: '@timestamp', + sortOrder: 'asc', + }); + expect(getTopMetricsMetricAgg().makeLabel(aggConfig)).toEqual( + 'First "bytes" value by "@timestamp"' + ); + }); + + it('should return a label with size if larger then 1', () => { + init({ + fieldName: 'bytes', + sortFieldName: '@timestamp', + sortOrder: 'asc', + size: 3, + }); + expect(getTopMetricsMetricAgg().makeLabel(aggConfig)).toEqual( + 'First 3 "bytes" values by "@timestamp"' + ); + }); + + it('should return a fieldName in getValueBucketPath', () => { + init({ + fieldName: 'bytes', + sortFieldName: '@timestamp', + sortOrder: 'asc', + size: 3, + }); + expect(getTopMetricsMetricAgg().getValueBucketPath(aggConfig)).toEqual('1[bytes]'); + }); + + it('produces the expected expression ast', () => { + init({ fieldName: 'machine.os', sortFieldName: '@timestamp' }); + expect(aggConfig.toExpressionAst()).toMatchInlineSnapshot(` + Object { + "chain": Array [ + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "field": Array [ + "machine.os", + ], + "id": Array [ + "1", + ], + "schema": Array [ + "metric", + ], + "size": Array [ + 1, + ], + "sortField": Array [ + "@timestamp", + ], + "sortOrder": Array [ + "desc", + ], + }, + "function": "aggTopMetrics", + "type": "function", + }, + ], + "type": "expression", + } + `); + }); + + describe('gets value from top metrics bucket', () => { + it('should return null if there is no hits', () => { + const bucket = { + '1': { + top: [], + }, + }; + + init({ fieldName: 'bytes' }); + expect(getTopMetricsMetricAgg().getValue(aggConfig, bucket)).toBe(null); + }); + + it('should return a single value if there is a single hit', () => { + const bucket = { + '1': { + top: [{ sort: [3], metrics: { bytes: 1024 } }], + }, + }; + + init({ fieldName: 'bytes' }); + expect(getTopMetricsMetricAgg().getValue(aggConfig, bucket)).toBe(1024); + }); + + it('should return an array of values if there is a multiple results', () => { + const bucket = { + '1': { + top: [ + { sort: [3], metrics: { bytes: 1024 } }, + { sort: [2], metrics: { bytes: 512 } }, + { sort: [1], metrics: { bytes: 256 } }, + ], + }, + }; + + init({ fieldName: 'bytes' }); + expect(getTopMetricsMetricAgg().getValue(aggConfig, bucket)).toEqual([1024, 512, 256]); + }); + }); +}); diff --git a/src/plugins/data/common/search/aggs/metrics/top_metrics.ts b/src/plugins/data/common/search/aggs/metrics/top_metrics.ts new file mode 100644 index 0000000000000..2079925e0435b --- /dev/null +++ b/src/plugins/data/common/search/aggs/metrics/top_metrics.ts @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import _ from 'lodash'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { i18n } from '@kbn/i18n'; +import { aggTopMetricsFnName } from './top_metrics_fn'; +import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; +import { METRIC_TYPES } from './metric_agg_types'; +import { KBN_FIELD_TYPES } from '../../../../common'; +import { BaseAggParams } from '../types'; + +export interface AggParamsTopMetrics extends BaseAggParams { + field: string; + sortField?: string; + sortOrder?: 'desc' | 'asc'; + size?: number; +} + +export const getTopMetricsMetricAgg = () => { + return new MetricAggType({ + name: METRIC_TYPES.TOP_METRICS, + expressionName: aggTopMetricsFnName, + title: i18n.translate('data.search.aggs.metrics.topMetricsTitle', { + defaultMessage: 'Top metrics', + }), + makeLabel(aggConfig) { + const isDescOrder = aggConfig.getParam('sortOrder').value === 'desc'; + const size = aggConfig.getParam('size'); + const field = aggConfig.getParam('field'); + const sortField = aggConfig.getParam('sortField'); + + if (isDescOrder) { + if (size > 1) { + return i18n.translate('data.search.aggs.metrics.topMetrics.descWithSizeLabel', { + defaultMessage: `Last {size} "{fieldName}" values by "{sortField}"`, + values: { + size, + fieldName: field?.displayName, + sortField: sortField?.displayName ?? '_score', + }, + }); + } else { + return i18n.translate('data.search.aggs.metrics.topMetrics.descNoSizeLabel', { + defaultMessage: `Last "{fieldName}" value by "{sortField}"`, + values: { + fieldName: field?.displayName, + sortField: sortField?.displayName ?? '_score', + }, + }); + } + } else { + if (size > 1) { + return i18n.translate('data.search.aggs.metrics.topMetrics.ascWithSizeLabel', { + defaultMessage: `First {size} "{fieldName}" values by "{sortField}"`, + values: { + size, + fieldName: field?.displayName, + sortField: sortField?.displayName ?? '_score', + }, + }); + } else { + return i18n.translate('data.search.aggs.metrics.topMetrics.ascNoSizeLabel', { + defaultMessage: `First "{fieldName}" value by "{sortField}"`, + values: { + fieldName: field?.displayName, + sortField: sortField?.displayName ?? '_score', + }, + }); + } + } + }, + params: [ + { + name: 'field', + type: 'field', + scriptable: false, + filterFieldTypes: [ + KBN_FIELD_TYPES.STRING, + KBN_FIELD_TYPES.IP, + KBN_FIELD_TYPES.BOOLEAN, + KBN_FIELD_TYPES.NUMBER, + KBN_FIELD_TYPES.DATE, + ], + write(agg, output) { + const field = agg.getParam('field'); + output.params.metrics = { field: field.name }; + }, + }, + { + name: 'size', + default: 1, + }, + { + name: 'sortField', + type: 'field', + scriptable: false, + filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE], + default(agg: IMetricAggConfig) { + return agg.getIndexPattern().timeFieldName; + }, + write: _.noop, // prevent default write, it is handled below + }, + { + name: 'sortOrder', + type: 'optioned', + default: 'desc', + options: [ + { + text: i18n.translate('data.search.aggs.metrics.topMetrics.descendingLabel', { + defaultMessage: 'Descending', + }), + value: 'desc', + }, + { + text: i18n.translate('data.search.aggs.metrics.topMetrics.ascendingLabel', { + defaultMessage: 'Ascending', + }), + value: 'asc', + }, + ], + write(agg, output) { + const sortField = agg.params.sortField; + const sortOrder = agg.params.sortOrder; + + if (sortField && sortOrder) { + output.params.sort = { + [sortField.name]: sortOrder.value, + }; + } else { + output.params.sort = '_score'; + } + }, + }, + ], + // override is needed to support top_metrics as an orderAgg of terms agg + getValueBucketPath(agg) { + const field = agg.getParam('field').name; + return `${agg.id}[${field}]`; + }, + getValue(agg, aggregate: Record) { + const metricFieldName = agg.getParam('field').name; + const results = aggregate[agg.id]?.top.map((result) => result.metrics[metricFieldName]) ?? []; + + if (results.length === 0) return null; + if (results.length === 1) return results[0]; + return results; + }, + }); +}; diff --git a/src/plugins/data/common/search/aggs/metrics/top_metrics_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/top_metrics_fn.test.ts new file mode 100644 index 0000000000000..848fccda283fa --- /dev/null +++ b/src/plugins/data/common/search/aggs/metrics/top_metrics_fn.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { functionWrapper } from '../test_helpers'; +import { aggTopMetrics } from './top_metrics_fn'; + +describe('agg_expression_functions', () => { + describe('aggTopMetrics', () => { + const fn = functionWrapper(aggTopMetrics()); + + test('fills in defaults when only required args are provided', () => { + const actual = fn({ + field: 'machine.os.keyword', + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "size": undefined, + "sortField": undefined, + "sortOrder": undefined, + }, + "schema": undefined, + "type": "top_metrics", + }, + } + `); + }); + + test('includes optional params when they are provided', () => { + const actual = fn({ + id: '1', + enabled: false, + schema: 'whatever', + field: 'machine.os.keyword', + sortOrder: 'asc', + size: 6, + sortField: 'bytes', + }); + + expect(actual.value).toMatchInlineSnapshot(` + Object { + "enabled": false, + "id": "1", + "params": Object { + "customLabel": undefined, + "field": "machine.os.keyword", + "json": undefined, + "size": 6, + "sortField": "bytes", + "sortOrder": "asc", + }, + "schema": "whatever", + "type": "top_metrics", + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + field: 'machine.os.keyword', + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual('{ "foo": true }'); + }); + }); +}); diff --git a/src/plugins/data/common/search/aggs/metrics/top_metrics_fn.ts b/src/plugins/data/common/search/aggs/metrics/top_metrics_fn.ts new file mode 100644 index 0000000000000..6fe9ba97fe448 --- /dev/null +++ b/src/plugins/data/common/search/aggs/metrics/top_metrics_fn.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; + +export const aggTopMetricsFnName = 'aggTopMetrics'; + +type Input = any; +type AggArgs = AggExpressionFunctionArgs; +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggTopMetricsFnName, + Input, + AggArgs, + Output +>; + +export const aggTopMetrics = (): FunctionDefinition => ({ + name: aggTopMetricsFnName, + help: i18n.translate('data.search.aggs.function.metrics.topMetrics.help', { + defaultMessage: 'Generates a serialized aggregation configuration for Top metrics.', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.topMetrics.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.metrics.topMetrics.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.topMetrics.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + field: { + types: ['string'], + required: true, + help: i18n.translate('data.search.aggs.metrics.topMetrics.field.help', { + defaultMessage: 'Field to use for this aggregation', + }), + }, + size: { + types: ['number'], + help: i18n.translate('data.search.aggs.metrics.topMetrics.size.help', { + defaultMessage: 'Number of top values to retrieve', + }), + }, + sortOrder: { + types: ['string'], + options: ['desc', 'asc'], + help: i18n.translate('data.search.aggs.metrics.topMetrics.sortOrder.help', { + defaultMessage: 'Order in which to return the results: asc or desc', + }), + }, + sortField: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.topMetrics.sortField.help', { + defaultMessage: 'Field to order results by', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.topMetrics.json.help', { + defaultMessage: 'Advanced JSON to include when the aggregation is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.metrics.topMetrics.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: METRIC_TYPES.TOP_METRICS, + params: { + ...rest, + }, + }, + }; + }, +}); diff --git a/src/plugins/data/common/search/aggs/param_types/field.ts b/src/plugins/data/common/search/aggs/param_types/field.ts index 940fdafd54875..b56787121f724 100644 --- a/src/plugins/data/common/search/aggs/param_types/field.ts +++ b/src/plugins/data/common/search/aggs/param_types/field.ts @@ -43,6 +43,7 @@ export class FieldParamType extends BaseParamType { this.filterFieldTypes = config.filterFieldTypes || '*'; this.onlyAggregatable = config.onlyAggregatable !== false; + this.scriptable = config.scriptable !== false; this.filterField = config.filterField; if (!config.write) { diff --git a/src/plugins/data/common/search/aggs/types.ts b/src/plugins/data/common/search/aggs/types.ts index cf9a6123b14c8..edc328bcb5099 100644 --- a/src/plugins/data/common/search/aggs/types.ts +++ b/src/plugins/data/common/search/aggs/types.ts @@ -93,6 +93,8 @@ import { import { AggParamsSampler } from './buckets/sampler'; import { AggParamsDiversifiedSampler } from './buckets/diversified_sampler'; import { AggParamsSignificantText } from './buckets/significant_text'; +import { AggParamsTopMetrics } from './metrics/top_metrics'; +import { aggTopMetrics } from './metrics/top_metrics_fn'; export type { IAggConfig, AggConfigSerialized } from './agg_config'; export type { CreateAggConfigParams, IAggConfigs } from './agg_configs'; @@ -187,6 +189,7 @@ export interface AggParamsMapping { [METRIC_TYPES.PERCENTILES]: AggParamsPercentiles; [METRIC_TYPES.SERIAL_DIFF]: AggParamsSerialDiff; [METRIC_TYPES.TOP_HITS]: AggParamsTopHit; + [METRIC_TYPES.TOP_METRICS]: AggParamsTopMetrics; } /** @@ -229,4 +232,5 @@ export interface AggFunctionsMapping { aggStdDeviation: ReturnType; aggSum: ReturnType; aggTopHit: ReturnType; + aggTopMetrics: ReturnType; } diff --git a/src/plugins/data/public/search/aggs/aggs_service.test.ts b/src/plugins/data/public/search/aggs/aggs_service.test.ts index 101c2c909c7e1..83328e196fa0a 100644 --- a/src/plugins/data/public/search/aggs/aggs_service.test.ts +++ b/src/plugins/data/public/search/aggs/aggs_service.test.ts @@ -54,7 +54,7 @@ describe('AggsService - public', () => { service.setup(setupDeps); const start = service.start(startDeps); expect(start.types.getAll().buckets.length).toBe(16); - expect(start.types.getAll().metrics.length).toBe(23); + expect(start.types.getAll().metrics.length).toBe(24); }); test('registers custom agg types', () => { @@ -71,7 +71,7 @@ describe('AggsService - public', () => { const start = service.start(startDeps); expect(start.types.getAll().buckets.length).toBe(17); expect(start.types.getAll().buckets.some(({ name }) => name === 'foo')).toBe(true); - expect(start.types.getAll().metrics.length).toBe(24); + expect(start.types.getAll().metrics.length).toBe(25); expect(start.types.getAll().metrics.some(({ name }) => name === 'bar')).toBe(true); }); }); diff --git a/src/plugins/vis_default_editor/public/components/agg_params_map.ts b/src/plugins/vis_default_editor/public/components/agg_params_map.ts index a61df61f2316c..283e1d7511b75 100644 --- a/src/plugins/vis_default_editor/public/components/agg_params_map.ts +++ b/src/plugins/vis_default_editor/public/components/agg_params_map.ts @@ -67,6 +67,11 @@ const metrics = { sortField: controls.TopSortFieldParamEditor, sortOrder: controls.OrderParamEditor, }, + [METRIC_TYPES.TOP_METRICS]: { + field: controls.FieldParamEditor, + sortField: controls.TopSortFieldParamEditor, + sortOrder: controls.OrderParamEditor, + }, [METRIC_TYPES.PERCENTILES]: { percents: controls.PercentilesEditor, }, diff --git a/src/plugins/vis_default_editor/public/components/controls/metric_agg.tsx b/src/plugins/vis_default_editor/public/components/controls/metric_agg.tsx index 1f844b5042474..2888d399bc014 100644 --- a/src/plugins/vis_default_editor/public/components/controls/metric_agg.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/metric_agg.tsx @@ -13,7 +13,14 @@ import { i18n } from '@kbn/i18n'; import { useAvailableOptions, useFallbackMetric, useValidation } from './utils'; import { AggParamEditorProps } from '../agg_param_props'; -const aggFilter = ['!top_hits', '!percentiles', '!percentile_ranks', '!median', '!std_dev']; +const aggFilter = [ + '!top_hits', + '!top_metrics', + '!percentiles', + '!percentile_ranks', + '!median', + '!std_dev', +]; const EMPTY_VALUE = 'EMPTY_VALUE'; const DEFAULT_OPTIONS = [{ text: '', value: EMPTY_VALUE, hidden: true }]; diff --git a/test/interpreter_functional/test_suites/run_pipeline/esaggs_topmetrics.ts b/test/interpreter_functional/test_suites/run_pipeline/esaggs_topmetrics.ts new file mode 100644 index 0000000000000..4f43709ba4a7e --- /dev/null +++ b/test/interpreter_functional/test_suites/run_pipeline/esaggs_topmetrics.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { ExpectExpression, expectExpressionProvider } from './helpers'; +import { FtrProviderContext } from '../../../functional/ftr_provider_context'; + +export default function ({ + getService, + updateBaselines, +}: FtrProviderContext & { updateBaselines: boolean }) { + let expectExpression: ExpectExpression; + + describe('esaggs_topmetrics', () => { + before(() => { + expectExpression = expectExpressionProvider({ getService, updateBaselines }); + }); + + const timeRange = { + from: '2015-09-21T00:00:00Z', + to: '2015-09-22T00:00:00Z', + }; + + describe('aggTopMetrics', () => { + it('can execute aggTopMetrics', async () => { + const expression = ` + kibana_context timeRange={timerange from='${timeRange.from}' to='${timeRange.to}'} + | esaggs index={indexPatternLoad id='logstash-*'} + aggs={aggTerms id="1" enabled=true schema="bucket" field="extension.raw"} + aggs={aggTopMetrics id="2" enabled=true schema="metric" field="bytes" sortField="@timestamp" sortOrder="desc" size=3 } + `; + const result = await expectExpression('aggTopMetrics', expression).getResponse(); + + expect(result.rows.map((r: { 'col-0-1': string }) => r['col-0-1'])).to.eql([ + 'jpg', + 'css', + 'png', + 'gif', + 'php', + ]); + + result.rows.forEach((r: { 'col-1-2': number[] }) => { + expect(r['col-1-2'].length).to.be(3); + expect( + r['col-1-2'].forEach((metric) => { + expect(typeof metric).to.be('number'); + }) + ); + }); + }); + + it('can execute aggTopMetrics with different sortOrder and size', async () => { + const expression = ` + kibana_context timeRange={timerange from='${timeRange.from}' to='${timeRange.to}'} + | esaggs index={indexPatternLoad id='logstash-*'} + aggs={aggTerms id="1" enabled=true schema="bucket" field="extension.raw"} + aggs={aggTopMetrics id="2" enabled=true schema="metric" field="bytes" sortField="@timestamp" sortOrder="asc" size=1 } + `; + const result = await expectExpression('aggTopMetrics', expression).getResponse(); + + expect(result.rows.map((r: { 'col-0-1': string }) => r['col-0-1'])).to.eql([ + 'jpg', + 'css', + 'png', + 'gif', + 'php', + ]); + + result.rows.forEach((r: { 'col-1-2': number[] }) => { + expect(typeof r['col-1-2']).to.be('number'); + }); + }); + + it('can use aggTopMetrics as an orderAgg of aggTerms', async () => { + const expressionSortBytesAsc = ` + kibana_context timeRange={timerange from='${timeRange.from}' to='${timeRange.to}'} + | esaggs index={indexPatternLoad id='logstash-*'} + aggs={aggTerms id="1" enabled=true schema="bucket" field="extension.raw" size=1 orderAgg={aggTopMetrics id="order" enabled=true schema="metric" field="bytes" sortField="@timestamp" sortOrder="asc" size=1}} + aggs={aggCount id="2" enabled=true schema="metric"} + `; + + const resultSortBytesAsc = await expectExpression( + 'sortBytesAsc', + expressionSortBytesAsc + ).getResponse(); + + const expressionSortBytesDesc = ` + kibana_context timeRange={timerange from='${timeRange.from}' to='${timeRange.to}'} + | esaggs index={indexPatternLoad id='logstash-*'} + aggs={aggTerms id="1" enabled=true schema="bucket" field="extension.raw" size=1 orderAgg={aggTopMetrics id="order" enabled=true schema="metric" field="bytes" sortField="@timestamp" sortOrder="desc" size=1}} + aggs={aggCount id="2" enabled=true schema="metric"} + `; + + const resultSortBytesDesc = await expectExpression( + 'sortBytesDesc', + expressionSortBytesDesc + ).getResponse(); + + expect(resultSortBytesAsc.rows.length).to.be(1); + expect(resultSortBytesAsc.rows[0]['col-0-1']).to.be('jpg'); + + expect(resultSortBytesDesc.rows.length).to.be(1); + expect(resultSortBytesDesc.rows[0]['col-0-1']).to.be('php'); + }); + }); + }); +} diff --git a/test/interpreter_functional/test_suites/run_pipeline/index.ts b/test/interpreter_functional/test_suites/run_pipeline/index.ts index 97387fc0a965f..e24563a5918eb 100644 --- a/test/interpreter_functional/test_suites/run_pipeline/index.ts +++ b/test/interpreter_functional/test_suites/run_pipeline/index.ts @@ -25,6 +25,7 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'Australia/North', defaultIndex: 'logstash-*', + 'bfetch:disableCompression': true, // makes it easier to debug while developing tests }); await browser.setWindowSize(1300, 900); await PageObjects.common.navigateToApp('settings'); @@ -47,5 +48,6 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid loadTestFile(require.resolve('./esaggs_sampler')); loadTestFile(require.resolve('./esaggs_significanttext')); loadTestFile(require.resolve('./esaggs_rareterms')); + loadTestFile(require.resolve('./esaggs_topmetrics')); }); } From 73f7ac419f75cfae5ab840a072aea95a4f0f72d0 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Mon, 7 Mar 2022 17:02:16 +0100 Subject: [PATCH 021/140] Fix Pagination is not working correctly for host by risk under hosts tab (#126609) * Fix Pagination is not working correctly for host by risk under hosts tab * Fix changing items per page doesn't work * Fix next page button enabled when the table is empty --- .../integration/hosts/host_risk_tab.spec.ts | 31 ++++- .../overview/risky_hosts_panel.spec.ts | 2 +- .../cypress/screens/hosts/host_risk.ts | 11 ++ .../cypress/tasks/host_risk.ts | 22 ++- .../components/paginated_table/index.test.tsx | 27 ++++ .../components/paginated_table/index.tsx | 14 +- .../containers/host_risk_score/index.tsx | 2 +- .../es_archives/risky_hosts/data.json | 129 +++++++++++++++++- 8 files changed, 222 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/hosts/host_risk_tab.spec.ts b/x-pack/plugins/security_solution/cypress/integration/hosts/host_risk_tab.spec.ts index 38a639e19c6b8..3af77036649aa 100644 --- a/x-pack/plugins/security_solution/cypress/integration/hosts/host_risk_tab.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/hosts/host_risk_tab.spec.ts @@ -7,14 +7,20 @@ import { cleanKibana } from '../../tasks/common'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; -import { navigateToHostRiskDetailTab } from '../../tasks/host_risk'; +import { + navigateToHostRiskDetailTab, + openRiskTableFilterAndSelectTheCriticalOption, + removeCritialFilter, + selectFiveItemsPerPageOption, +} from '../../tasks/host_risk'; import { HOST_BY_RISK_TABLE_CELL, - HOST_BY_RISK_TABLE_FILTER, - HOST_BY_RISK_TABLE_FILTER_CRITICAL, + HOST_BY_RISK_TABLE_HOSTNAME_CELL, + HOST_BY_RISK_TABLE_NEXT_PAGE_BUTTON, } from '../../screens/hosts/host_risk'; import { loginAndWaitForPage } from '../../tasks/login'; import { HOSTS_URL } from '../../urls/navigation'; +import { clearSearchBar, kqlSearch } from '../../tasks/security_header'; describe('risk tab', () => { before(() => { @@ -29,15 +35,30 @@ describe('risk tab', () => { }); it('renders the table', () => { + kqlSearch('host.name: "siem-kibana" {enter}'); cy.get(HOST_BY_RISK_TABLE_CELL).eq(3).should('have.text', 'siem-kibana'); cy.get(HOST_BY_RISK_TABLE_CELL).eq(4).should('have.text', '21.00'); cy.get(HOST_BY_RISK_TABLE_CELL).eq(5).should('have.text', 'Low'); + clearSearchBar(); }); it('filters the table', () => { - cy.get(HOST_BY_RISK_TABLE_FILTER).click(); - cy.get(HOST_BY_RISK_TABLE_FILTER_CRITICAL).click(); + openRiskTableFilterAndSelectTheCriticalOption(); cy.get(HOST_BY_RISK_TABLE_CELL).eq(3).should('not.have.text', 'siem-kibana'); + + removeCritialFilter(); + }); + + it('should be able to change items count per page', () => { + selectFiveItemsPerPageOption(); + + cy.get(HOST_BY_RISK_TABLE_HOSTNAME_CELL).should('have.length', 5); + }); + + it('should not allow page change when page is empty', () => { + kqlSearch('host.name: "nonexistent_host" {enter}'); + cy.get(HOST_BY_RISK_TABLE_NEXT_PAGE_BUTTON).should(`not.exist`); + clearSearchBar(); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts b/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts index 1c55a38b32495..652b3c1118b30 100644 --- a/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts @@ -69,7 +69,7 @@ describe('Risky Hosts Link Panel', () => { `${OVERVIEW_RISKY_HOSTS_LINKS} ${OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL}` ).should('not.exist'); cy.get(`${OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); - cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 1 host'); + cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 6 hosts'); changeSpace(testSpaceName); cy.visit(`/s/${testSpaceName}${OVERVIEW_URL}`); diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts index 58331518255df..3209200cf25a1 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts @@ -24,3 +24,14 @@ export const HOST_BY_RISK_TABLE_FILTER = '[data-test-subj="host-risk-filter-butt export const HOST_BY_RISK_TABLE_FILTER_CRITICAL = '[data-test-subj="host-risk-filter-item-Critical"]'; + +export const HOST_BY_RISK_TABLE_PERPAGE_BUTTON = + '[data-test-subj="loadingMoreSizeRowPopover"] button'; + +export const HOST_BY_RISK_TABLE_PERPAGE_OPTIONS = + '[data-test-subj="loadingMorePickSizeRow"] button'; + +export const HOST_BY_RISK_TABLE_NEXT_PAGE_BUTTON = + '[data-test-subj="numberedPagination"] [data-test-subj="pagination-button-next"]'; + +export const HOST_BY_RISK_TABLE_HOSTNAME_CELL = '[data-test-subj="render-content-host.name"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/host_risk.ts b/x-pack/plugins/security_solution/cypress/tasks/host_risk.ts index 7a357e8a5c7fb..afa04bb6de0ca 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/host_risk.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/host_risk.ts @@ -5,7 +5,15 @@ * 2.0. */ -import { LOADING_TABLE, RISK_DETAILS_NAV, RISK_FLYOUT_TRIGGER } from '../screens/hosts/host_risk'; +import { + HOST_BY_RISK_TABLE_FILTER, + HOST_BY_RISK_TABLE_FILTER_CRITICAL, + HOST_BY_RISK_TABLE_PERPAGE_BUTTON, + HOST_BY_RISK_TABLE_PERPAGE_OPTIONS, + LOADING_TABLE, + RISK_DETAILS_NAV, + RISK_FLYOUT_TRIGGER, +} from '../screens/hosts/host_risk'; export const navigateToHostRiskDetailTab = () => cy.get(RISK_DETAILS_NAV).click(); @@ -15,3 +23,15 @@ export const waitForTableToLoad = () => { cy.get(LOADING_TABLE).should('exist'); cy.get(LOADING_TABLE).should('not.exist'); }; + +export const openRiskTableFilterAndSelectTheCriticalOption = () => { + cy.get(HOST_BY_RISK_TABLE_FILTER).click(); + cy.get(HOST_BY_RISK_TABLE_FILTER_CRITICAL).click(); +}; +export const removeCritialFilter = () => { + cy.get(HOST_BY_RISK_TABLE_FILTER_CRITICAL).click(); +}; +export const selectFiveItemsPerPageOption = () => { + cy.get(HOST_BY_RISK_TABLE_PERPAGE_BUTTON).click(); + cy.get(HOST_BY_RISK_TABLE_PERPAGE_OPTIONS).first().click(); +}; diff --git a/x-pack/plugins/security_solution/public/common/components/paginated_table/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/paginated_table/index.test.tsx index 64c3584bc668c..0c09dce9c07cb 100644 --- a/x-pack/plugins/security_solution/public/common/components/paginated_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/paginated_table/index.test.tsx @@ -327,6 +327,33 @@ describe('Paginated Table Component', () => { ); expect(wrapper.find('[data-test-subj="loadingMoreSizeRowPopover"]').exists()).toBeFalsy(); }); + + test('Should hide pagination if totalCount is zero', () => { + const wrapper = mount( + + {'My test supplement.'}

} + headerTitle="Hosts" + headerTooltip="My test tooltip" + headerUnit="Test Unit" + itemsPerRow={rowItems} + limit={DEFAULT_MAX_TABLE_QUERY_SIZE} + loading={false} + loadPage={loadPage} + pageOfItems={mockData.Hosts.edges} + showMorePagesIndicator={true} + totalCount={0} + updateActivePage={updateActivePage} + updateLimitPagination={(limit) => updateLimitPagination({ limit })} + /> +
+ ); + + expect(wrapper.find('[data-test-subj="numberedPagination"]').exists()).toBeFalsy(); + }); }); describe('Events', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/paginated_table/index.tsx b/x-pack/plugins/security_solution/public/common/components/paginated_table/index.tsx index 6100c03d38bfa..310ab039057c2 100644 --- a/x-pack/plugins/security_solution/public/common/components/paginated_table/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/paginated_table/index.tsx @@ -303,12 +303,14 @@ const PaginatedTableComponent: FC = ({ - + {totalCount > 0 && ( + + )} {(isInspect || myLoading) && ( diff --git a/x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/index.tsx index 7f2c41f1414cf..516895d49b866 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/index.tsx @@ -180,7 +180,7 @@ export const useHostRiskScore = ({ factoryQueryType: HostsQueries.hostsRiskScore, filterQuery: createFilter(filterQuery), pagination: - cursorStart && querySize + cursorStart !== undefined && querySize !== undefined ? { cursorStart, querySize, diff --git a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json index cde819a836b0a..3e468d7a84ca2 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json @@ -13,7 +13,7 @@ "rule_risk": 42 } ] - }, + }, "host":{ "name":"siem-kibana" }, @@ -23,6 +23,131 @@ } } +{ + "type":"doc", + "value":{ + "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb71f", + "index":"ml_host_risk_score_latest_default", + "source":{ + "@timestamp":"2021-03-10T14:51:05.766Z", + "risk_stats": { + "risk_score": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + }, + "host":{ + "name":"fake-1" + }, + "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", + "risk":"Moderate" + } + } +} + +{ + "type":"doc", + "value":{ + "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb72f", + "index":"ml_host_risk_score_latest_default", + "source":{ + "@timestamp":"2021-03-10T14:51:05.766Z", + "risk_stats": { + "risk_score": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + }, + "host":{ + "name":"fake-2" + }, + "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", + "risk":"Moderate" + } + } +} + +{ + "type":"doc", + "value":{ + "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb73f", + "index":"ml_host_risk_score_latest_default", + "source":{ + "@timestamp":"2021-03-10T14:51:05.766Z", + "risk_stats": { + "risk_score": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + }, + "host":{ + "name":"fake-3" + }, + "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", + "risk":"Moderate" + } + } +} + +{ + "type":"doc", + "value":{ + "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", + "index":"ml_host_risk_score_latest_default", + "source":{ + "@timestamp":"2021-03-10T14:51:05.766Z", + "risk_stats": { + "risk_score": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + }, + "host":{ + "name":"fake-4" + }, + "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", + "risk":"Moderate" + } + } +} + +{ + "type":"doc", + "value":{ + "id":"a2cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb75f", + "index":"ml_host_risk_score_latest_default", + "source":{ + "@timestamp":"2021-03-10T14:51:05.766Z", + "risk_stats": { + "risk_score": 50, + "rule_risks": [ + { + "rule_name": "Unusual Linux Username", + "rule_risk": 42 + } + ] + }, + "host":{ + "name":"fake-5" + }, + "ingest_timestamp":"2021-03-09T18:02:08.319296053Z", + "risk":"Moderate" + } + } +} + { "type":"doc", "value":{ @@ -38,7 +163,7 @@ "rule_risk": 42 } ] - }, + }, "host":{ "name":"siem-kibana" }, From 004f0d4daa042afa2f1b9fbd74a44b9a4b4755b0 Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 7 Mar 2022 08:24:04 -0800 Subject: [PATCH 022/140] [type-summarizer/integration] fix flaky tests (#127038) --- .../src/lib/source_mapper.ts | 20 +++++++++++-------- .../tests/integration_tests/class.test.ts | 2 +- .../tests/integration_tests/function.test.ts | 6 ++++-- .../integration_tests/import_boundary.test.ts | 4 ++-- .../tests/integration_tests/interface.test.ts | 2 +- .../integration_tests/references.test.ts | 8 +++++--- .../integration_tests/type_alias.test.ts | 2 +- .../tests/integration_tests/variables.test.ts | 2 +- 8 files changed, 27 insertions(+), 19 deletions(-) diff --git a/packages/kbn-type-summarizer/src/lib/source_mapper.ts b/packages/kbn-type-summarizer/src/lib/source_mapper.ts index f6075684e04a6..0b0e69571469c 100644 --- a/packages/kbn-type-summarizer/src/lib/source_mapper.ts +++ b/packages/kbn-type-summarizer/src/lib/source_mapper.ts @@ -17,6 +17,8 @@ import { tryReadFile } from './helpers/fs'; import { parseJson } from './helpers/json'; import { isNodeModule } from './is_node_module'; +type SourceMapConsumerEntry = [ts.SourceFile, BasicSourceMapConsumer | undefined]; + export class SourceMapper { static async forSourceFiles( log: Logger, @@ -24,10 +26,8 @@ export class SourceMapper { repoRelativePackageDir: string, sourceFiles: readonly ts.SourceFile[] ) { - const consumers = new Map(); - - await Promise.all( - sourceFiles.map(async (sourceFile) => { + const entries = await Promise.all( + sourceFiles.map(async (sourceFile): Promise => { if (isNodeModule(dtsDir, sourceFile.fileName)) { return; } @@ -35,8 +35,7 @@ export class SourceMapper { const text = sourceFile.getText(); const match = text.match(/^\/\/#\s*sourceMappingURL=(.*)/im); if (!match) { - consumers.set(sourceFile, undefined); - return; + return [sourceFile, undefined]; } const relSourceFile = Path.relative(process.cwd(), sourceFile.fileName); @@ -50,11 +49,16 @@ export class SourceMapper { } const json = parseJson(sourceJson, `source map at [${relSourceMapPath}]`); - consumers.set(sourceFile, await new SourceMapConsumer(json)); - log.debug('loaded sourcemap for', relSourceFile); + return [sourceFile, await new SourceMapConsumer(json)]; }) ); + const consumers = new Map(entries.filter((e): e is SourceMapConsumerEntry => !!e)); + log.debug( + 'loaded sourcemaps for', + Array.from(consumers.keys()).map((s) => Path.relative(process.cwd(), s.fileName)) + ); + return new SourceMapper(consumers, repoRelativePackageDir); } diff --git a/packages/kbn-type-summarizer/tests/integration_tests/class.test.ts b/packages/kbn-type-summarizer/tests/integration_tests/class.test.ts index 84c1ee80c5f16..d2b39ab69d47b 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/class.test.ts +++ b/packages/kbn-type-summarizer/tests/integration_tests/class.test.ts @@ -69,7 +69,7 @@ it('prints basic class correctly', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] debug Ignoring 1 global declarations for \\"Record\\" debug Ignoring 5 global declarations for \\"Promise\\" " diff --git a/packages/kbn-type-summarizer/tests/integration_tests/function.test.ts b/packages/kbn-type-summarizer/tests/integration_tests/function.test.ts index 6afc04afe8faa..ec15d941ca153 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/function.test.ts +++ b/packages/kbn-type-summarizer/tests/integration_tests/function.test.ts @@ -74,8 +74,10 @@ it('prints the function declaration, including comments', async () => { } `); expect(result.logs).toMatchInlineSnapshot(` - "debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/bar.d.ts - debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts + "debug loaded sourcemaps for [ + 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/bar.d.ts', + 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' + ] " `); }); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/import_boundary.test.ts b/packages/kbn-type-summarizer/tests/integration_tests/import_boundary.test.ts index f23b6c3656d50..35cf08e297359 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/import_boundary.test.ts +++ b/packages/kbn-type-summarizer/tests/integration_tests/import_boundary.test.ts @@ -52,7 +52,7 @@ it('output type links to named import from node modules', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] " `); }); @@ -84,7 +84,7 @@ it('output type links to default import from node modules', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] " `); }); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/interface.test.ts b/packages/kbn-type-summarizer/tests/integration_tests/interface.test.ts index da53e91302eef..cc821f1c9fc90 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/interface.test.ts +++ b/packages/kbn-type-summarizer/tests/integration_tests/interface.test.ts @@ -55,7 +55,7 @@ it('prints the whole interface, including comments', async () => { } `); expect(result.logs).toMatchInlineSnapshot(` - "debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] debug Ignoring 5 global declarations for \\"Promise\\" " `); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/references.test.ts b/packages/kbn-type-summarizer/tests/integration_tests/references.test.ts index 1733b43694000..796bcd5fac3d1 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/references.test.ts +++ b/packages/kbn-type-summarizer/tests/integration_tests/references.test.ts @@ -59,9 +59,11 @@ it('collects references from source files which contribute to result', async () } `); expect(result.logs).toMatchInlineSnapshot(` - "debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/files/foo.d.ts - debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/files/index.d.ts - debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts + "debug loaded sourcemaps for [ + 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/files/foo.d.ts', + 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/files/index.d.ts', + 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' + ] debug Ignoring 5 global declarations for \\"Promise\\" debug Ignoring 4 global declarations for \\"Symbol\\" debug Ignoring 2 global declarations for \\"Component\\" diff --git a/packages/kbn-type-summarizer/tests/integration_tests/type_alias.test.ts b/packages/kbn-type-summarizer/tests/integration_tests/type_alias.test.ts index 79c2ea69b9477..f099bad9f3de6 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/type_alias.test.ts +++ b/packages/kbn-type-summarizer/tests/integration_tests/type_alias.test.ts @@ -36,7 +36,7 @@ it('prints basic type alias', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] " `); }); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/variables.test.ts b/packages/kbn-type-summarizer/tests/integration_tests/variables.test.ts index daa6abcc34c59..c51c9b0098b6c 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/variables.test.ts +++ b/packages/kbn-type-summarizer/tests/integration_tests/variables.test.ts @@ -62,7 +62,7 @@ it('prints basic variable exports with sourcemaps', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemap for packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] " `); }); From a4febd770916997b6355653089e7d7210bde0d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Mon, 7 Mar 2022 11:38:58 -0500 Subject: [PATCH 023/140] Allow access to ElasticsearchClient's child function from core's TS interface (#126731) * Expose child from core ElasticsearchClient * Update docs * Fix typecheck Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...-plugin-core-server.elasticsearchclient.md | 2 +- src/core/server/elasticsearch/client/types.ts | 2 +- src/core/server/server.api.md | 2 +- .../lib/wrap_scoped_cluster_client.test.ts | 25 +++++-------------- .../server/lib/wrap_scoped_cluster_client.ts | 5 ++-- x-pack/plugins/alerting/server/types.ts | 6 ----- 6 files changed, 11 insertions(+), 31 deletions(-) diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclient.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclient.md index 9a04a1d581765..1dfb1ab7a0b42 100644 --- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchclient.md +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchclient.md @@ -9,5 +9,5 @@ Client used to query the elasticsearch cluster. Signature: ```typescript -export declare type ElasticsearchClient = Omit; +export declare type ElasticsearchClient = Omit; ``` diff --git a/src/core/server/elasticsearch/client/types.ts b/src/core/server/elasticsearch/client/types.ts index 68fbc87193074..17248e491962d 100644 --- a/src/core/server/elasticsearch/client/types.ts +++ b/src/core/server/elasticsearch/client/types.ts @@ -15,7 +15,7 @@ import type { Client } from '@elastic/elasticsearch'; */ export type ElasticsearchClient = Omit< Client, - 'connectionPool' | 'serializer' | 'extend' | 'child' | 'close' | 'diagnostic' + 'connectionPool' | 'serializer' | 'extend' | 'close' | 'diagnostic' >; /** diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 5fe1942ed8453..6d5b06346225b 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -886,7 +886,7 @@ export { EcsEventOutcome } export { EcsEventType } // @public -export type ElasticsearchClient = Omit; +export type ElasticsearchClient = Omit; // @public export type ElasticsearchClientConfig = Pick & { diff --git a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts index 15f5a37edb910..d6101c9c6cec4 100644 --- a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts +++ b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts @@ -9,7 +9,6 @@ import { Client } from '@elastic/elasticsearch'; import { loggingSystemMock } from 'src/core/server/mocks'; import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; import { createWrappedScopedClusterClientFactory } from './wrap_scoped_cluster_client'; -import { ElasticsearchClientWithChild } from '../types'; const esQuery = { body: { query: { bool: { filter: { range: { '@timestamp': { gte: 0 } } } } } }, @@ -41,9 +40,7 @@ describe('wrapScopedClusterClient', () => { const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); - ( - scopedClusterClient.asInternalUser as unknown as jest.Mocked - ).child.mockReturnValue(childClient as unknown as Client); + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); const asInternalUserWrappedSearchFn = childClient.search; const wrappedSearchClient = createWrappedScopedClusterClientFactory({ @@ -62,9 +59,7 @@ describe('wrapScopedClusterClient', () => { const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); - ( - scopedClusterClient.asCurrentUser as unknown as jest.Mocked - ).child.mockReturnValue(childClient as unknown as Client); + scopedClusterClient.asCurrentUser.child.mockReturnValue(childClient as unknown as Client); const asCurrentUserWrappedSearchFn = childClient.search; const wrappedSearchClient = createWrappedScopedClusterClientFactory({ @@ -83,9 +78,7 @@ describe('wrapScopedClusterClient', () => { const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); - ( - scopedClusterClient.asInternalUser as unknown as jest.Mocked - ).child.mockReturnValue(childClient as unknown as Client); + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); const asInternalUserWrappedSearchFn = childClient.search; const wrappedSearchClient = createWrappedScopedClusterClientFactory({ @@ -106,9 +99,7 @@ describe('wrapScopedClusterClient', () => { const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); - ( - scopedClusterClient.asInternalUser as unknown as jest.Mocked - ).child.mockReturnValue(childClient as unknown as Client); + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); const asInternalUserWrappedSearchFn = childClient.search; asInternalUserWrappedSearchFn.mockRejectedValueOnce(new Error('something went wrong!')); @@ -127,9 +118,7 @@ describe('wrapScopedClusterClient', () => { const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); - ( - scopedClusterClient.asInternalUser as unknown as jest.Mocked - ).child.mockReturnValue(childClient as unknown as Client); + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); const asInternalUserWrappedSearchFn = childClient.search; // @ts-ignore incomplete return type asInternalUserWrappedSearchFn.mockResolvedValue({}); @@ -156,9 +145,7 @@ describe('wrapScopedClusterClient', () => { const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); - ( - scopedClusterClient.asInternalUser as unknown as jest.Mocked - ).child.mockReturnValue(childClient as unknown as Client); + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); const asInternalUserWrappedSearchFn = childClient.search; // @ts-ignore incomplete return type asInternalUserWrappedSearchFn.mockResolvedValue({ took: 333 }); diff --git a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts index dfe32a48ce438..2b71f95cd9f1c 100644 --- a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts +++ b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts @@ -21,7 +21,7 @@ import type { AggregationsAggregate, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IScopedClusterClient, ElasticsearchClient, Logger } from 'src/core/server'; -import { ElasticsearchClientWithChild, RuleExecutionMetrics } from '../types'; +import { RuleExecutionMetrics } from '../types'; import { Alert as Rule } from '../types'; type RuleInfo = Pick & { spaceId: string }; @@ -87,8 +87,7 @@ function wrapScopedClusterClient(opts: WrapScopedClusterClientOpts): IScopedClus function wrapEsClient(opts: WrapEsClientOpts): ElasticsearchClient { const { esClient, ...rest } = opts; - // Core hides access to .child via TS - const wrappedClient = (esClient as ElasticsearchClientWithChild).child({}); + const wrappedClient = esClient.child({}); // Mutating the functions we want to wrap wrappedClient.search = getWrappedSearchFn({ esClient: wrappedClient, ...rest }); diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 6b06f7efe3066..5499ba0c76caf 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -5,12 +5,10 @@ * 2.0. */ -import { Client } from '@elastic/elasticsearch'; import type { IRouter, RequestHandlerContext, SavedObjectReference, - ElasticsearchClient, IUiSettingsClient, } from 'src/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; @@ -48,10 +46,6 @@ import { IAbortableClusterClient } from './lib/create_abortable_es_client_factor export type WithoutQueryAndParams = Pick>; export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined; -export interface ElasticsearchClientWithChild extends ElasticsearchClient { - child: Client['child']; -} - /** * @public */ From 23ac12ba7b3da78356a32127b453a68708e86f50 Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Mon, 7 Mar 2022 17:39:36 +0100 Subject: [PATCH 024/140] [Workplace Search] Connect button no longer gets stuck on connection error (#126771) --- .../components/add_source/add_source_logic.test.ts | 2 ++ .../components/add_source/connect_instance.test.tsx | 3 +-- .../components/add_source/connect_instance.tsx | 13 ++++--------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts index 80f8a2fc18218..a633beac3a1c2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts @@ -664,11 +664,13 @@ describe('AddSourceLogic', () => { }); it('handles error', async () => { + const setButtonNotLoadingSpy = jest.spyOn(AddSourceLogic.actions, 'setButtonNotLoading'); http.post.mockReturnValue(Promise.reject('this is an error')); AddSourceLogic.actions.createContentSource(serviceType, successCallback, errorCallback); await nextTick(); + expect(setButtonNotLoadingSpy).toHaveBeenCalled(); expect(errorCallback).toHaveBeenCalled(); expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.test.tsx index 0ee80019ea720..0ae176dbef019 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.test.tsx @@ -36,9 +36,8 @@ describe('ConnectInstance', () => { const getSourceConnectData = jest.fn((_, redirectOauth) => { redirectOauth(); }); - const createContentSource = jest.fn((_, redirectFormCreated, handleFormSubmitError) => { + const createContentSource = jest.fn((_, redirectFormCreated) => { redirectFormCreated(); - handleFormSubmitError(); }); const credentialsSourceData = staticSourceData[13]; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx index a9e24c7b944ab..352addd8176d8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useEffect, FormEvent } from 'react'; +import React, { useEffect, FormEvent } from 'react'; import { useActions, useValues } from 'kea'; @@ -51,8 +51,6 @@ export const ConnectInstance: React.FC = ({ onFormCreated, header, }) => { - const [formLoading, setFormLoading] = useState(false); - const { hasPlatinumLicense } = useValues(LicensingLogic); const { @@ -64,7 +62,7 @@ export const ConnectInstance: React.FC = ({ setSourceIndexPermissionsValue, } = useActions(AddSourceLogic); - const { loginValue, passwordValue, indexPermissionsValue, subdomainValue } = + const { buttonLoading, loginValue, passwordValue, indexPermissionsValue, subdomainValue } = useValues(AddSourceLogic); const { isOrganization } = useValues(AppLogic); @@ -77,12 +75,9 @@ export const ConnectInstance: React.FC = ({ const redirectOauth = (oauthUrl: string) => window.location.replace(oauthUrl); const redirectFormCreated = () => onFormCreated(name); const onOauthFormSubmit = () => getSourceConnectData(serviceType, redirectOauth); - const handleFormSubmitError = () => setFormLoading(false); - const onCredentialsFormSubmit = () => - createContentSource(serviceType, redirectFormCreated, handleFormSubmitError); + const onCredentialsFormSubmit = () => createContentSource(serviceType, redirectFormCreated); const handleFormSubmit = (e: FormEvent) => { - setFormLoading(true); e.preventDefault(); const onSubmit = hasOauthRedirect ? onOauthFormSubmit : onCredentialsFormSubmit; onSubmit(); @@ -145,7 +140,7 @@ export const ConnectInstance: React.FC = ({ {permissionsExcluded && !hasPlatinumLicense && } - + {i18n.translate('xpack.enterpriseSearch.workplaceSearch.contentSource.connect.button', { defaultMessage: 'Connect {name}', values: { name }, From f47f89b58cb67fdf94fccce8bb6976697069ccaa Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Mon, 7 Mar 2022 17:40:15 +0100 Subject: [PATCH 025/140] [Workplace Search] Make SharePoint Online users start with the Oauth intro instead of the choice page (#126908) --- .../components/add_source/add_source.test.tsx | 32 +++++++++++++++++++ .../components/add_source/add_source.tsx | 13 +++++++- .../components/add_source/add_source_logic.ts | 1 + .../add_source/configuration_choice.test.tsx | 30 ++++++----------- .../add_source/configuration_choice.tsx | 31 ++++++++---------- .../views/content_sources/sources_router.tsx | 3 +- 6 files changed, 71 insertions(+), 39 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx index 4598ca337f4e2..76c6c3cfa9d59 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.test.tsx @@ -27,6 +27,7 @@ import { staticSourceData } from '../../source_data'; import { AddSource } from './add_source'; import { AddSourceSteps } from './add_source_logic'; import { ConfigCompleted } from './config_completed'; +import { ConfigurationChoice } from './configuration_choice'; import { ConfigurationIntro } from './configuration_intro'; import { ConfigureOauth } from './configure_oauth'; import { ConnectInstance } from './connect_instance'; @@ -71,6 +72,22 @@ describe('AddSourceList', () => { expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep); }); + it('renders default state correctly when there are multiple connector options', () => { + const wrapper = shallow( + + ); + wrapper.find(ConfigurationIntro).prop('advanceStep')(); + + expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.ChoiceStep); + }); + describe('layout', () => { it('renders the default workplace search layout when on an organization view', () => { setMockValues({ ...mockValues, isOrganization: true }); @@ -153,4 +170,19 @@ describe('AddSourceList', () => { expect(wrapper.find(Reauthenticate)).toHaveLength(1); }); + + it('renders Config Choice step', () => { + setMockValues({ + ...mockValues, + addSourceCurrentStep: AddSourceSteps.ChoiceStep, + }); + const wrapper = shallow(); + const advance = wrapper.find(ConfigurationChoice).prop('goToInternalStep'); + expect(advance).toBeDefined(); + if (advance) { + advance(); + } + + expect(setAddSourceStep).toHaveBeenCalledWith(AddSourceSteps.SaveConfigStep); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx index 1e9be74224c5e..f03c77290f22d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source.tsx @@ -21,9 +21,12 @@ import { import { NAV } from '../../../../constants'; import { SOURCES_PATH, getSourcesPath, getAddPath } from '../../../../routes'; +import { hasMultipleConnectorOptions } from '../../../../utils'; + import { AddSourceHeader } from './add_source_header'; import { AddSourceLogic, AddSourceProps, AddSourceSteps } from './add_source_logic'; import { ConfigCompleted } from './config_completed'; +import { ConfigurationChoice } from './configuration_choice'; import { ConfigurationIntro } from './configuration_intro'; import { ConfigureOauth } from './configure_oauth'; import { ConnectInstance } from './connect_instance'; @@ -51,6 +54,7 @@ export const AddSource: React.FC = (props) => { const goToSaveConfig = () => setAddSourceStep(AddSourceSteps.SaveConfigStep); const setConfigCompletedStep = () => setAddSourceStep(AddSourceSteps.ConfigCompletedStep); const goToConfigCompleted = () => saveSourceConfig(false, setConfigCompletedStep); + const goToChoice = () => setAddSourceStep(AddSourceSteps.ChoiceStep); const FORM_SOURCE_ADDED_SUCCESS_MESSAGE = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.contentSource.formSourceAddedSuccessMessage', { @@ -75,7 +79,11 @@ export const AddSource: React.FC = (props) => { return ( {addSourceCurrentStep === AddSourceSteps.ConfigIntroStep && ( - + )} {addSourceCurrentStep === AddSourceSteps.SaveConfigStep && ( = (props) => { {addSourceCurrentStep === AddSourceSteps.ReauthenticateStep && ( )} + {addSourceCurrentStep === AddSourceSteps.ChoiceStep && ( + + )} ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts index db0c5b9737263..92fab713a3fa0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts @@ -41,6 +41,7 @@ export enum AddSourceSteps { ConnectInstanceStep = 'Connect Instance', ConfigureOauthStep = 'Configure Oauth', ReauthenticateStep = 'Reauthenticate', + ChoiceStep = 'Choice', } export interface OauthParams { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.test.tsx index bfb916847d865..392ce175d271d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.test.tsx @@ -13,10 +13,6 @@ import { shallow } from 'enzyme'; import { EuiText, EuiButton } from '@elastic/eui'; -import { - PersonalDashboardLayout, - WorkplaceSearchPageTemplate, -} from '../../../../components/layout'; import { staticSourceData } from '../../source_data'; import { ConfigurationChoice } from './configuration_choice'; @@ -35,22 +31,6 @@ describe('ConfigurationChoice', () => { jest.clearAllMocks(); }); - describe('layout', () => { - it('renders the default workplace search layout when on an organization view', () => { - setMockValues({ ...mockValues, isOrganization: true }); - const wrapper = shallow(); - - expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate); - }); - - it('renders the personal dashboard layout when not in an organization', () => { - setMockValues({ ...mockValues, isOrganization: false }); - const wrapper = shallow(); - - expect(wrapper.type()).toEqual(PersonalDashboardLayout); - }); - }); - it('renders internal connector if available', () => { const wrapper = shallow(); @@ -64,6 +44,16 @@ describe('ConfigurationChoice', () => { button.simulate('click'); expect(navigateToUrl).toHaveBeenCalledWith('/sources/add/box/internal/'); }); + it('should call prop function when provided on internal connector click', () => { + const advanceSpy = jest.fn(); + const wrapper = shallow( + + ); + const button = wrapper.find(EuiButton); + button.simulate('click'); + expect(navigateToUrl).not.toHaveBeenCalled(); + expect(advanceSpy).toHaveBeenCalled(); + }); it('renders external connector if available', () => { const wrapper = shallow( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.tsx index 46a8998c9dd10..f5d6d51651dd4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configuration_choice.tsx @@ -9,16 +9,11 @@ import React from 'react'; import { useValues } from 'kea'; -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { KibanaLogic } from '../../../../../shared/kibana'; import { AppLogic } from '../../../../app_logic'; -import { - WorkplaceSearchPageTemplate, - PersonalDashboardLayout, -} from '../../../../components/layout'; -import { NAV } from '../../../../constants'; import { getAddPath, getSourcesPath } from '../../../../routes'; import { SourceDataItem } from '../../../../types'; @@ -26,6 +21,7 @@ import { AddSourceHeader } from './add_source_header'; interface ConfigurationIntroProps { sourceData: SourceDataItem; + goToInternalStep?: () => void; } export const ConfigurationChoice: React.FC = ({ @@ -36,15 +32,18 @@ export const ConfigurationChoice: React.FC = ({ internalConnectorAvailable, customConnectorAvailable, }, + goToInternalStep, }) => { const { isOrganization } = useValues(AppLogic); - const goToInternal = () => - KibanaLogic.values.navigateToUrl( - `${getSourcesPath( - `${getSourcesPath(getAddPath(serviceType), isOrganization)}/internal`, - isOrganization - )}/` - ); + const goToInternal = goToInternalStep + ? goToInternalStep + : () => + KibanaLogic.values.navigateToUrl( + `${getSourcesPath( + `${getSourcesPath(getAddPath(serviceType), isOrganization)}/internal`, + isOrganization + )}/` + ); const goToExternal = () => KibanaLogic.values.navigateToUrl( `${getSourcesPath( @@ -59,12 +58,10 @@ export const ConfigurationChoice: React.FC = ({ isOrganization )}/` ); - const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout; return ( - + <> - = ({ )} - + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx index c2cd58a90f209..e735119f687cc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.tsx @@ -90,9 +90,10 @@ export const SourcesRouter: React.FC = () => { : externalConnectorAvailable ? 'external' : 'custom'; + const showChoice = defaultOption !== 'internal' && hasMultipleConnectorOptions(sourceData); return ( - {hasMultipleConnectorOptions(sourceData) ? ( + {showChoice ? ( ) : ( From ccf16e99f990075738e3c04ee90e8eb958135fc5 Mon Sep 17 00:00:00 2001 From: Kevin Logan <56395104+kevinlog@users.noreply.github.com> Date: Mon, 7 Mar 2022 11:40:57 -0500 Subject: [PATCH 026/140] [Security Solution] Add metadta doc and capabilities to telemetry (#126680) --- .../server/lib/telemetry/__mocks__/index.ts | 1 + .../server/lib/telemetry/receiver.ts | 54 ++++++++++++++++++ .../server/lib/telemetry/tasks/endpoint.ts | 57 ++++++++++++++++--- .../server/lib/telemetry/types.ts | 38 +++++++++++++ 4 files changed, 143 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts index 4d9b67af6c311..e18d104b0d73a 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts @@ -44,6 +44,7 @@ export const createMockTelemetryReceiver = ( fetchTrustedApplications: jest.fn(), fetchEndpointList: jest.fn(), fetchDetectionRules: jest.fn().mockReturnValue({ body: null }), + fetchEndpointMetadata: jest.fn(), } as unknown as jest.Mocked; }; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index 6e24cea41b718..91054577656b1 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -80,6 +80,13 @@ export interface ITelemetryReceiver { TransportResult>, unknown> >; + fetchEndpointMetadata( + executeFrom: string, + executeTo: string + ): Promise< + TransportResult>, unknown> + >; + fetchDiagnosticAlerts( executeFrom: string, executeTo: string @@ -270,6 +277,53 @@ export class TelemetryReceiver implements ITelemetryReceiver { return this.esClient.search(query, { meta: true }); } + public async fetchEndpointMetadata(executeFrom: string, executeTo: string) { + if (this.esClient === undefined || this.esClient === null) { + throw Error('elasticsearch client is unavailable: cannot retrieve elastic endpoint metrics'); + } + + const query: SearchRequest = { + expand_wildcards: ['open' as const, 'hidden' as const], + index: `.ds-metrics-endpoint.metadata-*`, + ignore_unavailable: false, + size: 0, // no query results required - only aggregation quantity + body: { + query: { + range: { + '@timestamp': { + gte: executeFrom, + lt: executeTo, + }, + }, + }, + aggs: { + endpoint_metadata: { + terms: { + field: 'agent.id', + size: this.max_records, + }, + aggs: { + latest_metadata: { + top_hits: { + size: 1, + sort: [ + { + '@timestamp': { + order: 'desc' as const, + }, + }, + ], + }, + }, + }, + }, + }, + }, + }; + + return this.esClient.search(query, { meta: true }); + } + public async fetchDiagnosticAlerts(executeFrom: string, executeTo: string) { if (this.esClient === undefined || this.esClient === null) { throw Error('elasticsearch client is unavailable: cannot retrieve diagnostic alerts'); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts index e9cc36bbff907..c2c318debccda 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts @@ -11,6 +11,8 @@ import type { EndpointMetricsAggregation, EndpointPolicyResponseAggregation, EndpointPolicyResponseDocument, + EndpointMetadataAggregation, + EndpointMetadataDocument, ESClusterInfo, ESLicense, } from '../types'; @@ -188,7 +190,36 @@ export function createTelemetryEndpointTaskConfig(maxTelemetryBatch: number) { ) : new Map(); - /** STAGE 4 - Create the telemetry log records + /** STAGE 4 - Fetch Endpoint Agent Metadata + * + * Reads Endpoint Agent metadata out of the `.ds-metrics-endpoint.metadata` data stream + * and buckets them by Endpoint Agent id and sorts by the top hit. The EP agent will + * report its metadata once per day OR every time a policy change has occured. If + * a metadata document(s) exists for an EP agent we map to fleet agent and policy + */ + if (endpointData.endpointMetadata === undefined) { + logger.debug(`no endpoint metadata to report`); + } + + const { body: endpointMetadataResponse } = endpointData.endpointMetadata as unknown as { + body: EndpointMetadataAggregation; + }; + + if (endpointMetadataResponse.aggregations === undefined) { + logger.debug(`no endpoint metadata to report`); + } + + const endpointMetadata = + endpointMetadataResponse.aggregations.endpoint_metadata.buckets.reduce( + (cache, endpointAgentId) => { + const doc = endpointAgentId.latest_metadata.hits.hits[0]; + cache.set(endpointAgentId.key, doc); + return cache; + }, + new Map() + ); + + /** STAGE 5 - Create the telemetry log records * * Iterates through the endpoint metrics documents at STAGE 1 and joins them together * to form the telemetry log that is sent back to Elastic Security developers to @@ -199,6 +230,7 @@ export function createTelemetryEndpointTaskConfig(maxTelemetryBatch: number) { const telemetryPayloads = endpointMetrics.map((endpoint) => { let policyConfig = null; let failedPolicy = null; + let endpointMetadataById = null; const fleetAgentId = endpoint.endpoint_metrics.elastic.agent.id; const endpointAgentId = endpoint.endpoint_agent; @@ -212,6 +244,10 @@ export function createTelemetryEndpointTaskConfig(maxTelemetryBatch: number) { } } + if (endpointMetadata) { + endpointMetadataById = endpointMetadata.get(endpointAgentId); + } + const { cpu, memory, @@ -242,6 +278,10 @@ export function createTelemetryEndpointTaskConfig(maxTelemetryBatch: number) { }, endpoint_meta: { os: endpoint.endpoint_metrics.host.os, + capabilities: + endpointMetadataById !== null && endpointMetadataById !== undefined + ? endpointMetadataById._source.Endpoint.capabilities + : [], }, policy_config: endpointPolicyDetail !== null ? endpointPolicyDetail : {}, policy_response: @@ -265,7 +305,7 @@ export function createTelemetryEndpointTaskConfig(maxTelemetryBatch: number) { }); /** - * STAGE 5 - Send the documents + * STAGE 6 - Send the documents * * Send the documents in a batches of maxTelemetryBatch */ @@ -287,11 +327,13 @@ async function fetchEndpointData( executeFrom: string, executeTo: string ) { - const [fleetAgentsResponse, epMetricsResponse, policyResponse] = await Promise.allSettled([ - receiver.fetchFleetAgents(), - receiver.fetchEndpointMetrics(executeFrom, executeTo), - receiver.fetchEndpointPolicyResponses(executeFrom, executeTo), - ]); + const [fleetAgentsResponse, epMetricsResponse, policyResponse, endpointMetadata] = + await Promise.allSettled([ + receiver.fetchFleetAgents(), + receiver.fetchEndpointMetrics(executeFrom, executeTo), + receiver.fetchEndpointPolicyResponses(executeFrom, executeTo), + receiver.fetchEndpointMetadata(executeFrom, executeTo), + ]); return { fleetAgentsResponse: @@ -300,5 +342,6 @@ async function fetchEndpointData( : EmptyFleetAgentResponse, endpointMetrics: epMetricsResponse.status === 'fulfilled' ? epMetricsResponse.value : undefined, epPolicyResponse: policyResponse.status === 'fulfilled' ? policyResponse.value : undefined, + endpointMetadata: endpointMetadata.status === 'fulfilled' ? endpointMetadata.value : undefined, }; } diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts index 35b531ae6941c..c1c65a428f62d 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts @@ -243,6 +243,44 @@ interface EndpointMetricOS { full: string; } +// EP Metadata + +export interface EndpointMetadataAggregation { + hits: { + total: { value: number }; + }; + aggregations: { + endpoint_metadata: { + buckets: Array<{ key: string; doc_count: number; latest_metadata: EndpointMetadataHits }>; + }; + }; +} + +interface EndpointMetadataHits { + hits: { + total: { value: number }; + hits: EndpointMetadataDocument[]; + }; +} + +export interface EndpointMetadataDocument { + _source: { + '@timestamp': string; + agent: { + id: string; + version: string; + }; + Endpoint: { + capabilities: string[]; + }; + elastic: { + agent: { + id: string; + }; + }; + }; +} + // List HTTP Types export const GetTrustedAppsRequestSchema = { From 8a8bfd56e72fcc763d536f400ed58e7eaf319abd Mon Sep 17 00:00:00 2001 From: Max Kovalev Date: Mon, 7 Mar 2022 18:42:58 +0200 Subject: [PATCH 027/140] [Maps] Fixed double click issue when deleting a shape (#124661) * #117369 - Fixed double click issue when deleting a shape * #117369 - updated deleting shape method; redux changes; * #117369 - waiting state for adding shapes feature * #1173669 - refactoring; removed unused functions * 117369 - refactoring * 117369 - Updates for onDraw methods, now the selected shape is not reset * 117369 - made addNewFeatureToIndex to be called once * 117369 - refactoring * 117369 - refactoring and clean up * 117369 - removed then method from actions * 117369 - refactoring * 1177369 - refactoring * 117369 - refactoring; Added new state in redux * 117369 - refactoring * 117369 - renaming layerId to featureId Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/maps/common/constants.ts | 1 + .../maps/public/actions/map_actions.ts | 26 ++++++++++++++++--- .../plugins/maps/public/actions/ui_actions.ts | 15 +++++++++++ .../mb_map/draw_control/draw_control.tsx | 21 +++------------ .../draw_feature_control.tsx | 14 ++++++---- .../draw_feature_control/index.ts | 9 +++---- .../draw_filter_control.tsx | 2 +- .../mb_map/draw_control/index.ts | 25 ------------------ x-pack/plugins/maps/public/reducers/ui.ts | 14 ++++++++++ .../maps/public/selectors/ui_selectors.ts | 1 + 10 files changed, 70 insertions(+), 58 deletions(-) delete mode 100644 x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 6b9f32b92b9c1..1720f0ebdb558 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -144,6 +144,7 @@ export enum DRAW_SHAPE { LINE = 'LINE', SIMPLE_SELECT = 'SIMPLE_SELECT', DELETE = 'DELETE', + WAIT = 'WAIT', } export const AGG_DELIMITER = '_of_'; diff --git a/x-pack/plugins/maps/public/actions/map_actions.ts b/x-pack/plugins/maps/public/actions/map_actions.ts index 2abbfc0d076a1..cccb49f360622 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.ts @@ -14,6 +14,7 @@ import turfBooleanContains from '@turf/boolean-contains'; import { Filter } from '@kbn/es-query'; import { Query, TimeRange } from 'src/plugins/data/public'; import { Geometry, Position } from 'geojson'; +import { asyncForEach } from '@kbn/std'; import { DRAW_MODE, DRAW_SHAPE } from '../../common/constants'; import type { MapExtentState, MapViewContext } from '../reducers/map/types'; import { MapStoreState } from '../reducers/store'; @@ -63,9 +64,10 @@ import { DrawState, MapCenterAndZoom, MapExtent, Timeslice } from '../../common/ import { INITIAL_LOCATION } from '../../common/constants'; import { updateTooltipStateForLayer } from './tooltip_actions'; import { isVectorLayer, IVectorLayer } from '../classes/layers/vector_layer'; -import { SET_DRAW_MODE } from './ui_actions'; +import { SET_DRAW_MODE, pushDeletedFeatureId, clearDeletedFeatureIds } from './ui_actions'; import { expandToTileBoundaries } from '../classes/util/geo_tile_utils'; import { getToasts } from '../kibana_services'; +import { getDeletedFeatureIds } from '../selectors/ui_selectors'; export function setMapInitError(errorMessage: string) { return { @@ -321,6 +323,10 @@ export function updateEditShape(shapeToDraw: DRAW_SHAPE | null) { drawShape: shapeToDraw, }, }); + + if (shapeToDraw !== DRAW_SHAPE.DELETE) { + dispatch(clearDeletedFeatureIds()); + } }; } @@ -353,7 +359,7 @@ export function updateEditLayer(layerId: string | null) { }; } -export function addNewFeatureToIndex(geometry: Geometry | Position[]) { +export function addNewFeatureToIndex(geometries: Array) { return async ( dispatch: ThunkDispatch, getState: () => MapStoreState @@ -369,7 +375,10 @@ export function addNewFeatureToIndex(geometry: Geometry | Position[]) { } try { - await (layer as IVectorLayer).addFeature(geometry); + dispatch(updateEditShape(DRAW_SHAPE.WAIT)); + await asyncForEach(geometries, async (geometry) => { + await (layer as IVectorLayer).addFeature(geometry); + }); await dispatch(syncDataForLayerDueToDrawing(layer)); } catch (e) { getToasts().addError(e, { @@ -378,6 +387,7 @@ export function addNewFeatureToIndex(geometry: Geometry | Position[]) { }), }); } + dispatch(updateEditShape(DRAW_SHAPE.SIMPLE_SELECT)); }; } @@ -386,6 +396,12 @@ export function deleteFeatureFromIndex(featureId: string) { dispatch: ThunkDispatch, getState: () => MapStoreState ) => { + // There is a race condition where users can click on a previously deleted feature before layer has re-rendered after feature delete. + // Check ensures delete requests for previously deleted features are aborted. + if (getDeletedFeatureIds(getState()).includes(featureId)) { + return; + } + const editState = getEditState(getState()); const layerId = editState ? editState.layerId : undefined; if (!layerId) { @@ -395,8 +411,11 @@ export function deleteFeatureFromIndex(featureId: string) { if (!layer || !isVectorLayer(layer)) { return; } + try { + dispatch(updateEditShape(DRAW_SHAPE.WAIT)); await (layer as IVectorLayer).deleteFeature(featureId); + dispatch(pushDeletedFeatureId(featureId)); await dispatch(syncDataForLayerDueToDrawing(layer)); } catch (e) { getToasts().addError(e, { @@ -405,5 +424,6 @@ export function deleteFeatureFromIndex(featureId: string) { }), }); } + dispatch(updateEditShape(DRAW_SHAPE.DELETE)); }; } diff --git a/x-pack/plugins/maps/public/actions/ui_actions.ts b/x-pack/plugins/maps/public/actions/ui_actions.ts index 70e24283ef48f..1ffcf416f6f8f 100644 --- a/x-pack/plugins/maps/public/actions/ui_actions.ts +++ b/x-pack/plugins/maps/public/actions/ui_actions.ts @@ -24,6 +24,8 @@ export const SET_OPEN_TOC_DETAILS = 'SET_OPEN_TOC_DETAILS'; export const SHOW_TOC_DETAILS = 'SHOW_TOC_DETAILS'; export const HIDE_TOC_DETAILS = 'HIDE_TOC_DETAILS'; export const SET_DRAW_MODE = 'SET_DRAW_MODE'; +export const PUSH_DELETED_FEATURE_ID = 'PUSH_DELETED_FEATURE_ID'; +export const CLEAR_DELETED_FEATURE_IDS = 'CLEAR_DELETED_FEATURE_IDS'; export function exitFullScreen() { return { @@ -123,3 +125,16 @@ export function closeTimeslider() { dispatch(setQuery({ clearTimeslice: true })); }; } + +export function pushDeletedFeatureId(featureId: string) { + return { + type: PUSH_DELETED_FEATURE_ID, + featureId, + }; +} + +export function clearDeletedFeatureIds() { + return { + type: CLEAR_DELETED_FEATURE_IDS, + }; +} diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx index a404db91a942e..8cbfcd3a41e80 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx @@ -20,15 +20,6 @@ import { DRAW_SHAPE } from '../../../../common/constants'; import { DrawCircle, DRAW_CIRCLE_RADIUS_LABEL_STYLE } from './draw_circle'; import { DrawTooltip } from './draw_tooltip'; -const mbModeEquivalencies = new Map([ - ['simple_select', DRAW_SHAPE.SIMPLE_SELECT], - ['draw_rectangle', DRAW_SHAPE.BOUNDS], - ['draw_circle', DRAW_SHAPE.DISTANCE], - ['draw_polygon', DRAW_SHAPE.POLYGON], - ['draw_line_string', DRAW_SHAPE.LINE], - ['draw_point', DRAW_SHAPE.POINT], -]); - const DRAW_RECTANGLE = 'draw_rectangle'; const DRAW_CIRCLE = 'draw_circle'; const mbDrawModes = MapboxDraw.modes; @@ -41,7 +32,6 @@ export interface Props { onClick?: (event: MapMouseEvent, drawControl?: MapboxDraw) => void; mbMap: MbMap; enable: boolean; - updateEditShape: (shapeToDraw: DRAW_SHAPE) => void; } export class DrawControl extends Component { @@ -91,12 +81,6 @@ export class DrawControl extends Component { } }, 0); - _onModeChange = ({ mode }: { mode: string }) => { - if (mbModeEquivalencies.has(mode)) { - this.props.updateEditShape(mbModeEquivalencies.get(mode)!); - } - }; - _removeDrawControl() { // Do not remove draw control after mbMap.remove is called, causes execeptions and mbMap.remove cleans up all map resources. const isMapRemoved = !this.props.mbMap.loaded(); @@ -105,7 +89,6 @@ export class DrawControl extends Component { } this.props.mbMap.getCanvas().style.cursor = ''; - this.props.mbMap.off('draw.modechange', this._onModeChange); this.props.mbMap.off('draw.create', this._onDraw); if (this.props.onClick) { this.props.mbMap.off('click', this._onClick); @@ -118,7 +101,6 @@ export class DrawControl extends Component { if (!this._mbDrawControlAdded) { this.props.mbMap.addControl(this._mbDrawControl); this._mbDrawControlAdded = true; - this.props.mbMap.on('draw.modechange', this._onModeChange); this.props.mbMap.on('draw.create', this._onDraw); if (this.props.onClick) { @@ -144,6 +126,9 @@ export class DrawControl extends Component { this._mbDrawControl.changeMode(DRAW_POINT); } else if (this.props.drawShape === DRAW_SHAPE.DELETE) { this._mbDrawControl.changeMode(SIMPLE_SELECT); + } else if (this.props.drawShape === DRAW_SHAPE.WAIT) { + this.props.mbMap.getCanvas().style.cursor = 'wait'; + this._mbDrawControl.changeMode(SIMPLE_SELECT); } else { this._mbDrawControl.changeMode(SIMPLE_SELECT); } diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx index 6c7fe9f0ad213..b6ffacc491030 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx @@ -15,7 +15,7 @@ import { i18n } from '@kbn/i18n'; import * as jsts from 'jsts'; import { MapMouseEvent } from '@kbn/mapbox-gl'; import { getToasts } from '../../../../kibana_services'; -import { DrawControl } from '../'; +import { DrawControl } from '../draw_control'; import { DRAW_MODE, DRAW_SHAPE } from '../../../../../common/constants'; import { ILayer } from '../../../../classes/layers/layer'; import { EXCLUDE_CENTROID_FEATURES } from '../../../../classes/util/mb_filter_expressions'; @@ -29,9 +29,8 @@ export interface ReduxStateProps { } export interface ReduxDispatchProps { - addNewFeatureToIndex: (geometry: Geometry | Position[]) => void; + addNewFeatureToIndex: (geometries: Array) => void; deleteFeatureFromIndex: (featureId: string) => void; - disableDrawState: () => void; } export interface OwnProps { @@ -43,6 +42,7 @@ type Props = ReduxStateProps & ReduxDispatchProps & OwnProps; export class DrawFeatureControl extends Component { _onDraw = async (e: { features: Feature[] }, mbDrawControl: MapboxDraw) => { try { + const geometries: Array = []; e.features.forEach((feature: Feature) => { const { geometry } = geoJSONReader.read(feature); if (!geometry.isSimple() || !geometry.isValid()) { @@ -58,9 +58,13 @@ export class DrawFeatureControl extends Component { this.props.drawMode === DRAW_MODE.DRAW_POINTS ? feature.geometry.coordinates : feature.geometry; - this.props.addNewFeatureToIndex(featureGeom); + geometries.push(featureGeom); } }); + + if (geometries.length) { + this.props.addNewFeatureToIndex(geometries); + } } catch (error) { getToasts().addWarning( i18n.translate('xpack.maps.drawFeatureControl.unableToCreateFeature', { @@ -71,7 +75,6 @@ export class DrawFeatureControl extends Component { }) ); } finally { - this.props.disableDrawState(); try { mbDrawControl.deleteAll(); } catch (_e) { @@ -86,6 +89,7 @@ export class DrawFeatureControl extends Component { if (!this.props.editLayer || this.props.drawShape !== DRAW_SHAPE.DELETE) { return; } + const mbEditLayerIds = this.props.editLayer .getMbLayerIds() .filter((mbLayerId) => !!this.props.mbMap.getLayer(mbLayerId)); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/index.ts b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/index.ts index e1d703173fc2d..d2c369b4bd50a 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/index.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/index.ts @@ -15,7 +15,7 @@ import { ReduxStateProps, OwnProps, } from './draw_feature_control'; -import { addNewFeatureToIndex, deleteFeatureFromIndex, updateEditShape } from '../../../../actions'; +import { addNewFeatureToIndex, deleteFeatureFromIndex } from '../../../../actions'; import { MapStoreState } from '../../../../reducers/store'; import { getEditState, getLayerById } from '../../../../selectors/map_selectors'; import { getDrawMode } from '../../../../selectors/ui_selectors'; @@ -34,15 +34,12 @@ function mapDispatchToProps( dispatch: ThunkDispatch ): ReduxDispatchProps { return { - addNewFeatureToIndex(geometry: Geometry | Position[]) { - dispatch(addNewFeatureToIndex(geometry)); + addNewFeatureToIndex(geometries: Array) { + dispatch(addNewFeatureToIndex(geometries)); }, deleteFeatureFromIndex(featureId: string) { dispatch(deleteFeatureFromIndex(featureId)); }, - disableDrawState() { - dispatch(updateEditShape(null)); - }, }; } diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_filter_control/draw_filter_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_filter_control/draw_filter_control.tsx index 2f652506857d2..98d88d43fc65f 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_filter_control/draw_filter_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_filter_control/draw_filter_control.tsx @@ -20,7 +20,7 @@ import { roundCoordinates, } from '../../../../../common/elasticsearch_util'; import { getToasts } from '../../../../kibana_services'; -import { DrawControl } from '../'; +import { DrawControl } from '../draw_control'; import { DrawCircleProperties } from '../draw_circle'; export interface Props { diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts deleted file mode 100644 index b0f1941caec08..0000000000000 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts +++ /dev/null @@ -1,25 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ThunkDispatch } from 'redux-thunk'; -import { AnyAction } from 'redux'; -import { connect } from 'react-redux'; -import { updateEditShape } from '../../../actions'; -import { MapStoreState } from '../../../reducers/store'; -import { DrawControl } from './draw_control'; -import { DRAW_SHAPE } from '../../../../common/constants'; - -function mapDispatchToProps(dispatch: ThunkDispatch) { - return { - updateEditShape(shapeToDraw: DRAW_SHAPE) { - dispatch(updateEditShape(shapeToDraw)); - }, - }; -} - -const connected = connect(null, mapDispatchToProps)(DrawControl); -export { connected as DrawControl }; diff --git a/x-pack/plugins/maps/public/reducers/ui.ts b/x-pack/plugins/maps/public/reducers/ui.ts index f3f948bb96508..f0f22c5a8c4a9 100644 --- a/x-pack/plugins/maps/public/reducers/ui.ts +++ b/x-pack/plugins/maps/public/reducers/ui.ts @@ -19,6 +19,8 @@ import { SHOW_TOC_DETAILS, HIDE_TOC_DETAILS, SET_DRAW_MODE, + PUSH_DELETED_FEATURE_ID, + CLEAR_DELETED_FEATURE_IDS, } from '../actions'; import { DRAW_MODE } from '../../common/constants'; @@ -37,6 +39,7 @@ export type MapUiState = { isLayerTOCOpen: boolean; isTimesliderOpen: boolean; openTOCDetails: string[]; + deletedFeatureIds: string[]; }; export const DEFAULT_IS_LAYER_TOC_OPEN = true; @@ -51,6 +54,7 @@ export const DEFAULT_MAP_UI_STATE = { // storing TOC detail visibility outside of map.layerList because its UI state and not map rendering state. // This also makes for easy read/write access for embeddables. openTOCDetails: [], + deletedFeatureIds: [], }; // Reducer @@ -82,6 +86,16 @@ export function ui(state: MapUiState = DEFAULT_MAP_UI_STATE, action: any) { return layerId !== action.layerId; }), }; + case PUSH_DELETED_FEATURE_ID: + return { + ...state, + deletedFeatureIds: [...state.deletedFeatureIds, action.featureId], + }; + case CLEAR_DELETED_FEATURE_IDS: + return { + ...state, + deletedFeatureIds: [], + }; default: return state; } diff --git a/x-pack/plugins/maps/public/selectors/ui_selectors.ts b/x-pack/plugins/maps/public/selectors/ui_selectors.ts index 942a5190691a1..6bdf5a35679a7 100644 --- a/x-pack/plugins/maps/public/selectors/ui_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/ui_selectors.ts @@ -17,3 +17,4 @@ export const getIsTimesliderOpen = ({ ui }: MapStoreState): boolean => ui.isTime export const getOpenTOCDetails = ({ ui }: MapStoreState): string[] => ui.openTOCDetails; export const getIsFullScreen = ({ ui }: MapStoreState): boolean => ui.isFullScreen; export const getIsReadOnly = ({ ui }: MapStoreState): boolean => ui.isReadOnly; +export const getDeletedFeatureIds = ({ ui }: MapStoreState): string[] => ui.deletedFeatureIds; From f85f809efb813dfd6611d0c99527f09aecc6927b Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Mon, 7 Mar 2022 10:52:34 -0600 Subject: [PATCH 028/140] Add retries (#126480) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../services/transform/edit_flyout.ts | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/x-pack/test/functional/services/transform/edit_flyout.ts b/x-pack/test/functional/services/transform/edit_flyout.ts index fb1d77f7abc6c..0198a1d3d8c36 100644 --- a/x-pack/test/functional/services/transform/edit_flyout.ts +++ b/x-pack/test/functional/services/transform/edit_flyout.ts @@ -45,28 +45,32 @@ export function TransformEditFlyoutProvider({ getService }: FtrProviderContext) await testSubjects.existOrFail(`transformEditRetentionPolicySwitch`, { timeout: 1000, }); - const isEnabled = await testSubjects.isEnabled(`transformEditRetentionPolicySwitch`); - expect(isEnabled).to.eql( - expectedValue, - `Expected 'transformEditRetentionPolicySwitch' input to be '${ - expectedValue ? 'enabled' : 'disabled' - }' (got '${isEnabled ? 'enabled' : 'disabled'}')` - ); + await retry.tryForTime(5000, async () => { + const isEnabled = await testSubjects.isEnabled(`transformEditRetentionPolicySwitch`); + expect(isEnabled).to.eql( + expectedValue, + `Expected 'transformEditRetentionPolicySwitch' input to be '${ + expectedValue ? 'enabled' : 'disabled' + }' (got '${isEnabled ? 'enabled' : 'disabled'}')` + ); + }); }, async assertTransformEditFlyoutRetentionPolicyFieldSelectEnabled(expectedValue: boolean) { await testSubjects.existOrFail(`transformEditFlyoutRetentionPolicyFieldSelect`, { timeout: 1000, }); - const isEnabled = await testSubjects.isEnabled( - `transformEditFlyoutRetentionPolicyFieldSelect` - ); - expect(isEnabled).to.eql( - expectedValue, - `Expected 'transformEditFlyoutRetentionPolicyFieldSelect' input to be '${ - expectedValue ? 'enabled' : 'disabled' - }' (got '${isEnabled ? 'enabled' : 'disabled'}')` - ); + await retry.tryForTime(5000, async () => { + const isEnabled = await testSubjects.isEnabled( + `transformEditFlyoutRetentionPolicyFieldSelect` + ); + expect(isEnabled).to.eql( + expectedValue, + `Expected 'transformEditFlyoutRetentionPolicyFieldSelect' input to be '${ + expectedValue ? 'enabled' : 'disabled' + }' (got '${isEnabled ? 'enabled' : 'disabled'}')` + ); + }); }, async assertTransformEditFlyoutRetentionPolicyFieldSelectValue(expectedValue: string) { From fd55bbde092dd4d1e45843594a3d8f5e632ac0c0 Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Mon, 7 Mar 2022 08:57:49 -0800 Subject: [PATCH 029/140] Marking more methods on the alerting task runner as private (#124546) * Marking more methods on the alerting task runner as private * Another one --- .../alerting/server/task_runner/task_runner.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index c05bdc3cf7bd9..ccf9659a8e67d 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -142,7 +142,7 @@ export class TaskRunner< this.executionId = uuid.v4(); } - async getDecryptedAttributes( + private async getDecryptedAttributes( ruleId: string, spaceId: string ): Promise<{ apiKey: string | null; enabled: boolean }> { @@ -267,7 +267,7 @@ export class TaskRunner< } } - async executeAlert( + private async executeAlert( alertId: string, alert: CreatedAlert, executionHandler: ExecutionHandler @@ -283,7 +283,7 @@ export class TaskRunner< return executionHandler({ actionGroup, actionSubgroup, context, state, alertId }); } - async executeAlerts( + private async executeAlerts( fakeRequest: KibanaRequest, rule: SanitizedAlert, params: Params, @@ -548,7 +548,7 @@ export class TaskRunner< }; } - async validateAndExecuteRule( + private async validateAndExecuteRule( fakeRequest: KibanaRequest, apiKey: RawRule['apiKey'], rule: SanitizedAlert, @@ -574,7 +574,9 @@ export class TaskRunner< return this.executeAlerts(fakeRequest, rule, validatedParams, executionHandler, spaceId, event); } - async loadRuleAttributesAndRun(event: Event): Promise> { + private async loadRuleAttributesAndRun( + event: Event + ): Promise> { const { params: { alertId: ruleId, spaceId }, } = this.taskInstance; From ff90bd4d8602d91e3d53f2764c68a9133ef666fa Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Mon, 7 Mar 2022 19:33:30 +0200 Subject: [PATCH 030/140] Use execution context for Fullstory (#126780) * fix a couple bugs in context management add execution context to fullstory * Update execution_context_service.ts * stop and app name tests * Use execution context in fullstory * Fix user hash Report org id to FS * Use setUserVars for esorgid * pass orgid into identify * fix Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/cloud/public/fullstory.ts | 2 + .../plugins/cloud/public/plugin.test.mocks.ts | 1 + x-pack/plugins/cloud/public/plugin.test.ts | 91 +++++++++++++++---- x-pack/plugins/cloud/public/plugin.tsx | 64 +++++++++---- 4 files changed, 121 insertions(+), 37 deletions(-) diff --git a/x-pack/plugins/cloud/public/fullstory.ts b/x-pack/plugins/cloud/public/fullstory.ts index 4f76abf540cae..602b1c4cc63d3 100644 --- a/x-pack/plugins/cloud/public/fullstory.ts +++ b/x-pack/plugins/cloud/public/fullstory.ts @@ -15,9 +15,11 @@ export interface FullStoryDeps { } export type FullstoryUserVars = Record; +export type FullstoryVars = Record; export interface FullStoryApi { identify(userId: string, userVars?: FullstoryUserVars): void; + setVars(pageName: string, vars?: FullstoryVars): void; setUserVars(userVars?: FullstoryUserVars): void; event(eventName: string, eventProperties: Record): void; } diff --git a/x-pack/plugins/cloud/public/plugin.test.mocks.ts b/x-pack/plugins/cloud/public/plugin.test.mocks.ts index b79fb1bc65130..1c185d0194912 100644 --- a/x-pack/plugins/cloud/public/plugin.test.mocks.ts +++ b/x-pack/plugins/cloud/public/plugin.test.mocks.ts @@ -11,6 +11,7 @@ import type { FullStoryDeps, FullStoryApi, FullStoryService } from './fullstory' export const fullStoryApiMock: jest.Mocked = { event: jest.fn(), setUserVars: jest.fn(), + setVars: jest.fn(), identify: jest.fn(), }; export const initializeFullStoryMock = jest.fn(() => ({ diff --git a/x-pack/plugins/cloud/public/plugin.test.ts b/x-pack/plugins/cloud/public/plugin.test.ts index 1eef581610f00..edbf724e25390 100644 --- a/x-pack/plugins/cloud/public/plugin.test.ts +++ b/x-pack/plugins/cloud/public/plugin.test.ts @@ -12,6 +12,7 @@ import { securityMock } from '../../security/public/mocks'; import { fullStoryApiMock, initializeFullStoryMock } from './plugin.test.mocks'; import { CloudPlugin, CloudConfigType, loadFullStoryUserId } from './plugin'; import { Observable, Subject } from 'rxjs'; +import { KibanaExecutionContext } from 'kibana/public'; describe('Cloud Plugin', () => { describe('#setup', () => { @@ -24,12 +25,12 @@ describe('Cloud Plugin', () => { config = {}, securityEnabled = true, currentUserProps = {}, - currentAppId$ = undefined, + currentContext$ = undefined, }: { config?: Partial; securityEnabled?: boolean; currentUserProps?: Record; - currentAppId$?: Observable; + currentContext$?: Observable; }) => { const initContext = coreMock.createPluginInitializerContext({ id: 'cloudId', @@ -51,8 +52,8 @@ describe('Cloud Plugin', () => { const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); - if (currentAppId$) { - coreStart.application.currentAppId$ = currentAppId$; + if (currentContext$) { + coreStart.executionContext.context$ = currentContext$; } coreSetup.getStartServices.mockResolvedValue([coreStart, {}, undefined]); @@ -94,44 +95,98 @@ describe('Cloud Plugin', () => { }); expect(fullStoryApiMock.identify).toHaveBeenCalledWith( - '03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4', + '5ef112cfdae3dea57097bc276e275b2816e73ef2a398dc0ffaf5b6b4e3af2041', { version_str: 'version', version_major_int: -1, version_minor_int: -1, version_patch_int: -1, + org_id_str: 'cloudId', } ); }); - it('calls FS.setUserVars everytime an app changes', async () => { - const currentAppId$ = new Subject(); + it('user hash includes org id', async () => { + await setupPlugin({ + config: { full_story: { enabled: true, org_id: 'foo' }, id: 'esOrg1' }, + currentUserProps: { + username: '1234', + }, + }); + + const hashId1 = fullStoryApiMock.identify.mock.calls[0][0]; + + await setupPlugin({ + config: { full_story: { enabled: true, org_id: 'foo' }, id: 'esOrg2' }, + currentUserProps: { + username: '1234', + }, + }); + + const hashId2 = fullStoryApiMock.identify.mock.calls[1][0]; + + expect(hashId1).not.toEqual(hashId2); + }); + + it('calls FS.setVars everytime an app changes', async () => { + const currentContext$ = new Subject(); const { plugin } = await setupPlugin({ config: { full_story: { enabled: true, org_id: 'foo' } }, currentUserProps: { username: '1234', }, - currentAppId$, + currentContext$, }); - expect(fullStoryApiMock.setUserVars).not.toHaveBeenCalled(); - currentAppId$.next('App1'); - expect(fullStoryApiMock.setUserVars).toHaveBeenCalledWith({ + // takes the app name + expect(fullStoryApiMock.setVars).not.toHaveBeenCalled(); + currentContext$.next({ + name: 'App1', + description: '123', + }); + + expect(fullStoryApiMock.setVars).toHaveBeenCalledWith('page', { + pageName: 'App1', app_id_str: 'App1', }); - currentAppId$.next(); - expect(fullStoryApiMock.setUserVars).toHaveBeenCalledWith({ - app_id_str: 'unknown', + + // context clear + currentContext$.next({}); + expect(fullStoryApiMock.setVars).toHaveBeenCalledWith('page', { + pageName: 'App1', + app_id_str: 'App1', }); - currentAppId$.next('App2'); - expect(fullStoryApiMock.setUserVars).toHaveBeenCalledWith({ + // different app + currentContext$.next({ + name: 'App2', + page: 'page2', + id: '123', + }); + expect(fullStoryApiMock.setVars).toHaveBeenCalledWith('page', { + pageName: 'App2:page2', app_id_str: 'App2', + page_str: 'page2', + ent_id_str: '123', + }); + + // Back to first app + currentContext$.next({ + name: 'App1', + page: 'page3', + id: '123', + }); + + expect(fullStoryApiMock.setVars).toHaveBeenCalledWith('page', { + pageName: 'App1:page3', + app_id_str: 'App1', + page_str: 'page3', + ent_id_str: '123', }); - expect(currentAppId$.observers.length).toBe(1); + expect(currentContext$.observers.length).toBe(1); plugin.stop(); - expect(currentAppId$.observers.length).toBe(0); + expect(currentContext$.observers.length).toBe(0); }); it('does not call FS.identify when security is not available', async () => { diff --git a/x-pack/plugins/cloud/public/plugin.tsx b/x-pack/plugins/cloud/public/plugin.tsx index 991a7c1f8b565..89f24971de25c 100644 --- a/x-pack/plugins/cloud/public/plugin.tsx +++ b/x-pack/plugins/cloud/public/plugin.tsx @@ -13,11 +13,12 @@ import { PluginInitializerContext, HttpStart, IBasePath, - ApplicationStart, + ExecutionContextStart, } from 'src/core/public'; import { i18n } from '@kbn/i18n'; import useObservable from 'react-use/lib/useObservable'; import { BehaviorSubject, Subscription } from 'rxjs'; +import { compact, isUndefined, omitBy } from 'lodash'; import type { AuthenticatedUser, SecurityPluginSetup, @@ -83,8 +84,9 @@ export interface CloudSetup { } interface SetupFullstoryDeps extends CloudSetupDependencies { - application?: Promise; + executionContextPromise?: Promise; basePath: IBasePath; + esOrgId?: string; } interface SetupChatDeps extends Pick { @@ -103,11 +105,16 @@ export class CloudPlugin implements Plugin { } public setup(core: CoreSetup, { home, security }: CloudSetupDependencies) { - const application = core.getStartServices().then(([coreStart]) => { - return coreStart.application; + const executionContextPromise = core.getStartServices().then(([coreStart]) => { + return coreStart.executionContext; }); - this.setupFullstory({ basePath: core.http.basePath, security, application }).catch((e) => + this.setupFullstory({ + basePath: core.http.basePath, + security, + executionContextPromise, + esOrgId: this.config.id, + }).catch((e) => // eslint-disable-next-line no-console console.debug(`Error setting up FullStory: ${e.toString()}`) ); @@ -223,9 +230,14 @@ export class CloudPlugin implements Plugin { return user?.roles.includes('superuser') ?? true; } - private async setupFullstory({ basePath, security, application }: SetupFullstoryDeps) { - const { enabled, org_id: orgId } = this.config.full_story; - if (!enabled || !orgId) { + private async setupFullstory({ + basePath, + security, + executionContextPromise, + esOrgId, + }: SetupFullstoryDeps) { + const { enabled, org_id: fsOrgId } = this.config.full_story; + if (!enabled || !fsOrgId) { return; // do not load any fullstory code in the browser if not enabled } @@ -243,7 +255,7 @@ export class CloudPlugin implements Plugin { const { fullStory, sha256 } = initializeFullStory({ basePath, - orgId, + orgId: fsOrgId, packageInfo: this.initializerContext.env.packageInfo, }); @@ -252,16 +264,29 @@ export class CloudPlugin implements Plugin { // This needs to be called syncronously to be sure that we populate the user ID soon enough to make sessions merging // across domains work if (userId) { - // Do the hashing here to keep it at clear as possible in our source code that we do not send literal user IDs - const hashedId = sha256(userId.toString()); - application - ?.then(async () => { - const appStart = await application; - this.appSubscription = appStart.currentAppId$.subscribe((appId) => { - // Update the current application every time it changes - fullStory.setUserVars({ - app_id_str: appId ?? 'unknown', - }); + // Join the cloud org id and the user to create a truly unique user id. + // The hashing here is to keep it at clear as possible in our source code that we do not send literal user IDs + const hashedId = sha256(esOrgId ? `${esOrgId}:${userId}` : `${userId}`); + + executionContextPromise + ?.then(async (executionContext) => { + this.appSubscription = executionContext.context$.subscribe((context) => { + const { name, page, id } = context; + // Update the current context every time it changes + fullStory.setVars( + 'page', + omitBy( + { + // Read about the special pageName property + // https://help.fullstory.com/hc/en-us/articles/1500004101581-FS-setVars-API-Sending-custom-page-data-to-FullStory + pageName: `${compact([name, page]).join(':')}`, + app_id_str: name ?? 'unknown', + page_str: page, + ent_id_str: id, + }, + isUndefined + ) + ); }); }) .catch((e) => { @@ -282,6 +307,7 @@ export class CloudPlugin implements Plugin { version_major_int: parsedVer[0] ?? -1, version_minor_int: parsedVer[1] ?? -1, version_patch_int: parsedVer[2] ?? -1, + org_id_str: esOrgId, }); } } catch (e) { From 935fcc2d3a8ab00bcb1bbc774dcbb79af641bc82 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 7 Mar 2022 17:50:58 +0000 Subject: [PATCH 031/140] skip flaky suite (#126658) --- test/functional_ccs/apps/discover/_data_view_ccs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional_ccs/apps/discover/_data_view_ccs.ts b/test/functional_ccs/apps/discover/_data_view_ccs.ts index 91d9cb2faf681..5a04e0e2a7532 100644 --- a/test/functional_ccs/apps/discover/_data_view_ccs.ts +++ b/test/functional_ccs/apps/discover/_data_view_ccs.ts @@ -27,7 +27,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.click('saveIndexPatternButton'); }; - describe('discover integration with data view editor', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/126658 + describe.skip('discover integration with data view editor', function describeIndexTests() { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); From b2e419d7077c0761c554dbe083ccb7fe7ce9dfcf Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Mon, 7 Mar 2022 11:16:27 -0700 Subject: [PATCH 032/140] [Reporting] Improve error logging for rescheduled jobs (#126737) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/lib/tasks/monitor_reports.ts | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/reporting/server/lib/tasks/monitor_reports.ts b/x-pack/plugins/reporting/server/lib/tasks/monitor_reports.ts index 1d406d7a5cc62..56e12d7d5512b 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/monitor_reports.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/monitor_reports.ts @@ -92,31 +92,42 @@ export class MonitorReportsTask implements ReportingTask { return; } - const { - _id: jobId, - _source: { process_expiration: processExpiration, status }, - } = recoveredJob; + const report = new SavedReport({ ...recoveredJob, ...recoveredJob._source }); + const { _id: jobId, process_expiration: processExpiration, status } = report; + const eventLog = this.reporting.getEventLogger(report); if (![statuses.JOB_STATUS_PENDING, statuses.JOB_STATUS_PROCESSING].includes(status)) { - throw new Error(`Invalid job status in the monitoring search result: ${status}`); // only pending or processing jobs possibility need rescheduling + const invalidStatusError = new Error( + `Invalid job status in the monitoring search result: ${status}` + ); // only pending or processing jobs possibility need rescheduling + this.logger.error(invalidStatusError); + eventLog.logError(invalidStatusError); + + // fatal: can not reschedule the job + throw invalidStatusError; } if (status === statuses.JOB_STATUS_PENDING) { - this.logger.info( + const migratingJobError = new Error( `${jobId} was scheduled in a previous version and left in [${status}] status. Rescheduling...` ); + this.logger.error(migratingJobError); + eventLog.logError(migratingJobError); } if (status === statuses.JOB_STATUS_PROCESSING) { const expirationTime = moment(processExpiration); const overdueValue = moment().valueOf() - expirationTime.valueOf(); - this.logger.info( + const overdueExpirationError = new Error( `${jobId} status is [${status}] and the expiration time was [${overdueValue}ms] ago. Rescheduling...` ); + this.logger.error(overdueExpirationError); + eventLog.logError(overdueExpirationError); } + eventLog.logRetry(); + // clear process expiration and set status to pending - const report = new SavedReport({ ...recoveredJob, ...recoveredJob._source }); await reportingStore.prepareReportForRetry(report); // if there is a version conflict response, this just throws and logs an error // clear process expiration and reschedule @@ -154,8 +165,6 @@ export class MonitorReportsTask implements ReportingTask { const newTask = await this.reporting.scheduleTask(task); - this.reporting.getEventLogger({ _id: task.id, ...task }, newTask).logRetry(); - return newTask; } From 629e0f3995ed74702ec91ad4d605e3c14ed6ea27 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 7 Mar 2022 18:24:55 +0000 Subject: [PATCH 033/140] skip flaky suite (#126027) --- test/functional/apps/management/_scripted_fields_filter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/management/_scripted_fields_filter.ts b/test/functional/apps/management/_scripted_fields_filter.ts index 82d1590819750..4f6d1a41d0523 100644 --- a/test/functional/apps/management/_scripted_fields_filter.ts +++ b/test/functional/apps/management/_scripted_fields_filter.ts @@ -17,7 +17,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['settings']); - describe('filter scripted fields', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/126027 + describe.skip('filter scripted fields', function describeIndexTests() { before(async function () { // delete .kibana index and then wait for Kibana to re-create it await browser.setWindowSize(1200, 800); From 064078235ec9a307b557691e016bf455e04e6a27 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 7 Mar 2022 18:33:00 +0000 Subject: [PATCH 034/140] [ML] Adding data recognizer module config cache (#126338) * [ML] Adding data recognizer module config cache * adding comment about cache Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../models/data_recognizer/data_recognizer.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts index 3df5016f560c0..4a621bc5f608b 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts +++ b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts @@ -124,6 +124,17 @@ export class DataRecognizer { private _resultsService: ReturnType; private _calculateModelMemoryLimit: ReturnType; + /** + * A temporary cache of configs loaded from disk and from save object service. + * The configs from disk will not change while kibana is running. + * The configs from saved objects could potentially change while an instance of + * DataRecognizer exists, if a fleet package containing modules is installed. + * However the chance of this happening is very low and so the benefit of using + * this cache outweighs the risk of the cache being out of date during the short + * existence of a DataRecognizer instance. + */ + private _configCache: Config[] | null = null; + /** * List of the module jobs that require model memory estimation */ @@ -181,6 +192,10 @@ export class DataRecognizer { } private async _loadConfigs(): Promise { + if (this._configCache !== null) { + return this._configCache; + } + const configs: Config[] = []; const dirs = await this._listDirs(this._modulesDir); await Promise.all( @@ -211,7 +226,9 @@ export class DataRecognizer { isSavedObject: true, })); - return [...configs, ...savedObjectConfigs]; + this._configCache = [...configs, ...savedObjectConfigs]; + + return this._configCache; } private async _loadSavedObjectModules() { From de3ae9bfec3a5b72acb808d3308e2d715ba89115 Mon Sep 17 00:00:00 2001 From: "Mark J. Hoy" Date: Mon, 7 Mar 2022 14:33:10 -0500 Subject: [PATCH 035/140] Remove License Requirement for Enterprise Search App Search Meta Engines (#127046) * Remove license for enterprise search meta engines * missed a flag for the unit test --- .../app_search/utils/role/get_role_abilities.test.ts | 6 +++--- .../app_search/utils/role/get_role_abilities.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/get_role_abilities.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/get_role_abilities.test.ts index 60d0dcc0c5911..0d5e4e9824f17 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/get_role_abilities.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/get_role_abilities.test.ts @@ -23,6 +23,7 @@ describe('getRoleAbilities', () => { // Has access canViewAccountCredentials: true, canManageEngines: true, + canManageMetaEngines: true, // Does not have access canViewMetaEngines: false, canViewEngineAnalytics: false, @@ -35,7 +36,6 @@ describe('getRoleAbilities', () => { canViewMetaEngineSourceEngines: false, canViewSettings: false, canViewRoleMappings: false, - canManageMetaEngines: false, canManageLogSettings: false, canManageSettings: false, canManageEngineCrawler: false, @@ -81,10 +81,10 @@ describe('getRoleAbilities', () => { expect(myRole.canManageMetaEngines).toEqual(true); }); - it('returns false when the user can manage any engines but the account does not have a platinum license', () => { + it('returns true when the user can manage any engines but the account does not have a platinum license', () => { const myRole = getRoleAbilities({ ...mockRole, ...canManageEngines }, false); - expect(myRole.canManageMetaEngines).toEqual(false); + expect(myRole.canManageMetaEngines).toEqual(true); }); it('returns false when has a platinum license but the user cannot manage any engines', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/get_role_abilities.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/get_role_abilities.ts index ef3e22d851f38..15cba16ab0434 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/get_role_abilities.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/role/get_role_abilities.ts @@ -49,7 +49,7 @@ export const getRoleAbilities = (role: Account['role'], hasPlatinumLicense = fal canViewSettings: myRole.can('view', 'account_settings'), canViewRoleMappings: myRole.can('view', 'role_mappings'), canManageEngines: myRole.can('manage', 'account_engines'), - canManageMetaEngines: hasPlatinumLicense && myRole.can('manage', 'account_engines'), + canManageMetaEngines: myRole.can('manage', 'account_engines'), canManageLogSettings: myRole.can('manage', 'account_log_settings'), canManageSettings: myRole.can('manage', 'account_settings'), canManageEngineCrawler: myRole.can('manage', 'engine_crawler'), From a24b1fc9576ffc05e0402065990a2ab60899ba7c Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 7 Mar 2022 15:13:04 -0500 Subject: [PATCH 036/140] Don't submit empty seed_urls or sitemap_urls when making a partial crawl request (#126972) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...crawl_custom_settings_flyout_logic.test.ts | 20 +++++++++++++++- .../crawl_custom_settings_flyout_logic.ts | 24 +++++++++++++------ .../components/crawler/crawler_logic.ts | 2 +- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.test.ts index ddfc23b5aa628..bb20e0e639aa2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.test.ts @@ -297,7 +297,25 @@ describe('CrawlCustomSettingsFlyoutLogic', () => { }); describe('startCustomCrawl', () => { - it('starts a custom crawl with the user set values', async () => { + it('can start a custom crawl for selected domains', async () => { + mount({ + includeSitemapsInRobotsTxt: true, + maxCrawlDepth: 5, + selectedDomainUrls: ['https://www.elastic.co', 'https://swiftype.com'], + }); + jest.spyOn(CrawlerLogic.actions, 'startCrawl'); + + CrawlCustomSettingsFlyoutLogic.actions.startCustomCrawl(); + await nextTick(); + + expect(CrawlerLogic.actions.startCrawl).toHaveBeenCalledWith({ + domain_allowlist: ['https://www.elastic.co', 'https://swiftype.com'], + max_crawl_depth: 5, + sitemap_discovery_disabled: false, + }); + }); + + it('can start a custom crawl selected domains, sitemaps, and seed urls', async () => { mount({ includeSitemapsInRobotsTxt: true, maxCrawlDepth: 5, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.ts index f22dcc7487af3..3b04e1b28c17e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawl_custom_settings_flyout/crawl_custom_settings_flyout_logic.ts @@ -11,7 +11,7 @@ import { flashAPIErrors } from '../../../../../shared/flash_messages'; import { HttpLogic } from '../../../../../shared/http'; import { EngineLogic } from '../../../engine'; -import { CrawlerLogic } from '../../crawler_logic'; +import { CrawlerLogic, CrawlRequestOverrides } from '../../crawler_logic'; import { DomainConfig, DomainConfigFromServer } from '../../types'; import { domainConfigServerToClient } from '../../utils'; import { extractDomainAndEntryPointFromUrl } from '../add_domain/utils'; @@ -213,13 +213,23 @@ export const CrawlCustomSettingsFlyoutLogic = kea< actions.fetchDomainConfigData(); }, startCustomCrawl: () => { - CrawlerLogic.actions.startCrawl({ - domain_allowlist: values.selectedDomainUrls, - max_crawl_depth: values.maxCrawlDepth, - seed_urls: [...values.selectedEntryPointUrls, ...values.customEntryPointUrls], - sitemap_urls: [...values.selectedSitemapUrls, ...values.customSitemapUrls], + const overrides: CrawlRequestOverrides = { sitemap_discovery_disabled: !values.includeSitemapsInRobotsTxt, - }); + max_crawl_depth: values.maxCrawlDepth, + domain_allowlist: values.selectedDomainUrls, + }; + + const seedUrls = [...values.selectedEntryPointUrls, ...values.customEntryPointUrls]; + if (seedUrls.length > 0) { + overrides.seed_urls = seedUrls; + } + + const sitemapUrls = [...values.selectedSitemapUrls, ...values.customSitemapUrls]; + if (sitemapUrls.length > 0) { + overrides.sitemap_urls = sitemapUrls; + } + + CrawlerLogic.actions.startCrawl(overrides); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_logic.ts index 68b1cb6ec9b26..2d1b8a9e7aa27 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_logic.ts @@ -33,7 +33,7 @@ const ACTIVE_STATUSES = [ CrawlerStatus.Canceling, ]; -interface CrawlRequestOverrides { +export interface CrawlRequestOverrides { domain_allowlist?: string[]; max_crawl_depth?: number; seed_urls?: string[]; From cb7088816afe851e01e5a295955c0f29de49c961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Mon, 7 Mar 2022 21:29:18 +0100 Subject: [PATCH 037/140] [Telemetry] Check permissions when requesting telemetry (#126238) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../imported_interface_from_export/index.ts | 2 +- .../telemetry_collectors/stats_collector.ts | 2 +- src/plugins/dashboard/tsconfig.json | 1 + src/plugins/home/kibana.json | 2 +- .../__snapshots__/home.test.tsx.snap | 196 --------- .../__snapshots__/welcome.test.tsx.snap | 384 +----------------- .../application/components/home.test.tsx | 2 - .../public/application/components/home.tsx | 10 +- .../public/application/components/home_app.js | 2 - .../components/welcome.test.mocks.ts | 17 + .../application/components/welcome.test.tsx | 51 +-- .../public/application/components/welcome.tsx | 96 +---- .../public/application/kibana_services.ts | 4 +- src/plugins/home/public/index.ts | 2 + src/plugins/home/public/mocks.ts | 7 +- src/plugins/home/public/plugin.test.mocks.ts | 3 + src/plugins/home/public/plugin.test.ts | 13 + src/plugins/home/public/plugin.ts | 12 +- src/plugins/home/public/services/index.ts | 3 + .../home/public/services/welcome/index.ts | 10 + .../services/welcome/welcome_service.mocks.ts | 36 ++ .../services/welcome/welcome_service.test.ts | 86 ++++ .../services/welcome/welcome_service.ts | 63 +++ src/plugins/home/tsconfig.json | 3 +- src/plugins/telemetry/kibana.json | 1 + src/plugins/telemetry/public/plugin.ts | 17 +- .../render_welcome_telemetry_notice.test.ts | 32 ++ .../render_welcome_telemetry_notice.tsx | 80 ++++ src/plugins/telemetry/server/plugin.ts | 13 +- src/plugins/telemetry/server/routes/index.ts | 8 +- .../server/routes/telemetry_opt_in_stats.ts | 1 - .../routes/telemetry_usage_stats.test.ts | 95 ++++- .../server/routes/telemetry_usage_stats.ts | 21 +- .../server/telemetry_collection/get_kibana.ts | 12 +- .../get_local_stats.test.ts | 3 +- .../telemetry_collection/get_local_stats.ts | 4 +- src/plugins/telemetry/tsconfig.json | 4 +- .../server/plugin.test.ts | 17 +- .../server/plugin.ts | 13 +- .../server/types.ts | 14 +- src/plugins/usage_collection/README.mdx | 3 +- .../server/collector/collector.ts | 25 +- .../server/collector/collector_set.test.ts | 262 +----------- .../server/collector/collector_set.ts | 37 +- .../server/collector/index.ts | 1 - .../server/collector/types.ts | 77 +--- .../server/collector/usage_collector.ts | 12 +- src/plugins/usage_collection/server/index.ts | 1 - src/plugins/usage_collection/server/mocks.ts | 16 +- src/plugins/usage_collection/server/plugin.ts | 18 +- .../server/routes/stats/stats.ts | 8 +- src/plugins/visualizations/tsconfig.json | 1 + .../collectors/get_settings_collector.ts | 1 - .../collectors/get_usage_collector.ts | 11 +- ...egister_monitoring_telemetry_collection.ts | 10 +- .../task_manager_usage_collector.test.ts | 6 +- .../get_stats_with_xpack.test.ts | 3 - .../translations/translations/ja-JP.json | 6 - .../translations/translations/zh-CN.json | 6 - .../apis/telemetry/telemetry.ts | 131 ++++++ 60 files changed, 715 insertions(+), 1262 deletions(-) create mode 100644 src/plugins/home/public/application/components/welcome.test.mocks.ts create mode 100644 src/plugins/home/public/services/welcome/index.ts create mode 100644 src/plugins/home/public/services/welcome/welcome_service.mocks.ts create mode 100644 src/plugins/home/public/services/welcome/welcome_service.test.ts create mode 100644 src/plugins/home/public/services/welcome/welcome_service.ts create mode 100644 src/plugins/telemetry/public/render_welcome_telemetry_notice.test.ts create mode 100644 src/plugins/telemetry/public/render_welcome_telemetry_notice.tsx diff --git a/src/fixtures/telemetry_collectors/imported_interface_from_export/index.ts b/src/fixtures/telemetry_collectors/imported_interface_from_export/index.ts index 222a89bf32298..991f8336e7020 100644 --- a/src/fixtures/telemetry_collectors/imported_interface_from_export/index.ts +++ b/src/fixtures/telemetry_collectors/imported_interface_from_export/index.ts @@ -11,7 +11,7 @@ import { createUsageCollectionSetupMock } from '../../../plugins/usage_collectio const { makeUsageCollector } = createUsageCollectionSetupMock(); -export const myCollector = makeUsageCollector({ +export const myCollector = makeUsageCollector({ type: 'importing_from_export_collector', isReady: () => true, fetch() { diff --git a/src/fixtures/telemetry_collectors/stats_collector.ts b/src/fixtures/telemetry_collectors/stats_collector.ts index c8f513a07253b..6046973f42e84 100644 --- a/src/fixtures/telemetry_collectors/stats_collector.ts +++ b/src/fixtures/telemetry_collectors/stats_collector.ts @@ -19,7 +19,7 @@ interface Usage { * We should collect them when the schema is defined. */ -export const myCollectorWithSchema = makeStatsCollector({ +export const myCollectorWithSchema = makeStatsCollector({ type: 'my_stats_collector_with_schema', isReady: () => true, fetch() { diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 55049447aee57..862bed9d667a0 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -24,6 +24,7 @@ { "path": "../navigation/tsconfig.json" }, { "path": "../saved_objects_tagging_oss/tsconfig.json" }, { "path": "../saved_objects/tsconfig.json" }, + { "path": "../screenshot_mode/tsconfig.json" }, { "path": "../ui_actions/tsconfig.json" }, { "path": "../charts/tsconfig.json" }, { "path": "../discover/tsconfig.json" }, diff --git a/src/plugins/home/kibana.json b/src/plugins/home/kibana.json index d8c09ab5e80c6..02b33e814e2a1 100644 --- a/src/plugins/home/kibana.json +++ b/src/plugins/home/kibana.json @@ -8,6 +8,6 @@ "server": true, "ui": true, "requiredPlugins": ["dataViews", "share", "urlForwarding"], - "optionalPlugins": ["usageCollection", "telemetry", "customIntegrations"], + "optionalPlugins": ["usageCollection", "customIntegrations"], "requiredBundles": ["kibanaReact"] } diff --git a/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap b/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap index ab6ad1b6cc0c5..43d8f935221b3 100644 --- a/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap +++ b/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap @@ -374,202 +374,6 @@ exports[`home isNewKibanaInstance should set isNewKibanaInstance to false when t exports[`home isNewKibanaInstance should set isNewKibanaInstance to true when there are no index patterns 1`] = ` `; diff --git a/src/plugins/home/public/application/components/__snapshots__/welcome.test.tsx.snap b/src/plugins/home/public/application/components/__snapshots__/welcome.test.tsx.snap index 17f7d2520e862..861e0ee895887 100644 --- a/src/plugins/home/public/application/components/__snapshots__/welcome.test.tsx.snap +++ b/src/plugins/home/public/application/components/__snapshots__/welcome.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render a Welcome screen with no telemetry disclaimer 1`] = ` +exports[`should render a Welcome screen 1`] = `
`; - -exports[`should render a Welcome screen with the telemetry disclaimer 1`] = ` - -
-
-
- - - - - -

- -

-
- -
-
-
- - - - - - - - - - - - - - - - - -
-
-
-`; - -exports[`should render a Welcome screen with the telemetry disclaimer when optIn is false 1`] = ` - -
-
-
- - - - - -

- -

-
- -
-
-
- - - - - - - - - - - - - - - - - -
-
-
-`; - -exports[`should render a Welcome screen with the telemetry disclaimer when optIn is true 1`] = ` - -
-
-
- - - - - -

- -

-
- -
-
-
- - - - - - - - - - - - - - - - - -
-
-
-`; - -exports[`should render a Welcome screen without the opt in/out link when user cannot change optIn status 1`] = ` - -
-
-
- - - - - -

- -

-
- -
-
-
- - - - - - - - - - - - - -
-
-
-`; diff --git a/src/plugins/home/public/application/components/home.test.tsx b/src/plugins/home/public/application/components/home.test.tsx index 9983afa3d4d61..f27a286488c2b 100644 --- a/src/plugins/home/public/application/components/home.test.tsx +++ b/src/plugins/home/public/application/components/home.test.tsx @@ -12,7 +12,6 @@ import type { HomeProps } from './home'; import { Home } from './home'; import { FeatureCatalogueCategory } from '../../services'; -import { telemetryPluginMock } from '../../../../telemetry/public/mocks'; import { Welcome } from './welcome'; let mockHasIntegrationsPermission = true; @@ -57,7 +56,6 @@ describe('home', () => { setItem: jest.fn(), }, urlBasePath: 'goober', - telemetry: telemetryPluginMock.createStartContract(), addBasePath(url) { return `base_path/${url}`; }, diff --git a/src/plugins/home/public/application/components/home.tsx b/src/plugins/home/public/application/components/home.tsx index fdf04ea580653..1fb0b3c790ab7 100644 --- a/src/plugins/home/public/application/components/home.tsx +++ b/src/plugins/home/public/application/components/home.tsx @@ -10,7 +10,6 @@ import React, { Component } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; -import type { TelemetryPluginStart } from 'src/plugins/telemetry/public'; import { KibanaPageTemplate, OverviewPageFooter } from '../../../../kibana_react/public'; import { HOME_APP_BASE_PATH } from '../../../common/constants'; import type { FeatureCatalogueEntry, FeatureCatalogueSolution } from '../../services'; @@ -29,7 +28,6 @@ export interface HomeProps { solutions: FeatureCatalogueSolution[]; localStorage: Storage; urlBasePath: string; - telemetry: TelemetryPluginStart; hasUserDataView: () => Promise; } @@ -175,13 +173,7 @@ export class Home extends Component { } private renderWelcome() { - return ( - this.skipWelcome()} - urlBasePath={this.props.urlBasePath} - telemetry={this.props.telemetry} - /> - ); + return this.skipWelcome()} urlBasePath={this.props.urlBasePath} />; } public render() { diff --git a/src/plugins/home/public/application/components/home_app.js b/src/plugins/home/public/application/components/home_app.js index 62df479ecbfdf..a634573aaf21e 100644 --- a/src/plugins/home/public/application/components/home_app.js +++ b/src/plugins/home/public/application/components/home_app.js @@ -26,7 +26,6 @@ export function HomeApp({ directories, solutions }) { getBasePath, addBasePath, environmentService, - telemetry, dataViewsService, } = getServices(); const environment = environmentService.getEnvironment(); @@ -75,7 +74,6 @@ export function HomeApp({ directories, solutions }) { solutions={solutions} localStorage={localStorage} urlBasePath={getBasePath()} - telemetry={telemetry} hasUserDataView={() => dataViewsService.hasUserDataView()} /> diff --git a/src/plugins/home/public/application/components/welcome.test.mocks.ts b/src/plugins/home/public/application/components/welcome.test.mocks.ts new file mode 100644 index 0000000000000..fc9854bae3199 --- /dev/null +++ b/src/plugins/home/public/application/components/welcome.test.mocks.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { welcomeServiceMock } from '../../services/welcome/welcome_service.mocks'; + +jest.doMock('../kibana_services', () => ({ + getServices: () => ({ + addBasePath: (path: string) => `root${path}`, + trackUiMetric: () => {}, + welcomeService: welcomeServiceMock.create(), + }), +})); diff --git a/src/plugins/home/public/application/components/welcome.test.tsx b/src/plugins/home/public/application/components/welcome.test.tsx index b042a91e58c9d..3400b4bfcdb75 100644 --- a/src/plugins/home/public/application/components/welcome.test.tsx +++ b/src/plugins/home/public/application/components/welcome.test.tsx @@ -8,58 +8,11 @@ import React from 'react'; import { shallow } from 'enzyme'; +import './welcome.test.mocks'; import { Welcome } from './welcome'; -import { telemetryPluginMock } from '../../../../telemetry/public/mocks'; -jest.mock('../kibana_services', () => ({ - getServices: () => ({ - addBasePath: (path: string) => `root${path}`, - trackUiMetric: () => {}, - }), -})); - -test('should render a Welcome screen with the telemetry disclaimer', () => { - const telemetry = telemetryPluginMock.createStartContract(); - const component = shallow( {}} telemetry={telemetry} />); - - expect(component).toMatchSnapshot(); -}); - -test('should render a Welcome screen with the telemetry disclaimer when optIn is true', () => { - const telemetry = telemetryPluginMock.createStartContract(); - telemetry.telemetryService.getIsOptedIn = jest.fn().mockReturnValue(true); - const component = shallow( {}} telemetry={telemetry} />); - - expect(component).toMatchSnapshot(); -}); - -test('should render a Welcome screen with the telemetry disclaimer when optIn is false', () => { - const telemetry = telemetryPluginMock.createStartContract(); - telemetry.telemetryService.getIsOptedIn = jest.fn().mockReturnValue(false); - const component = shallow( {}} telemetry={telemetry} />); - - expect(component).toMatchSnapshot(); -}); - -test('should render a Welcome screen with no telemetry disclaimer', () => { +test('should render a Welcome screen', () => { const component = shallow( {}} />); expect(component).toMatchSnapshot(); }); - -test('should render a Welcome screen without the opt in/out link when user cannot change optIn status', () => { - const telemetry = telemetryPluginMock.createStartContract(); - telemetry.telemetryService.getCanChangeOptInStatus = jest.fn().mockReturnValue(false); - const component = shallow( {}} telemetry={telemetry} />); - - expect(component).toMatchSnapshot(); -}); - -test('fires opt-in seen when mounted', () => { - const telemetry = telemetryPluginMock.createStartContract(); - const mockSetOptedInNoticeSeen = jest.fn(); - telemetry.telemetryNotifications.setOptedInNoticeSeen = mockSetOptedInNoticeSeen; - shallow( {}} telemetry={telemetry} />); - - expect(mockSetOptedInNoticeSeen).toHaveBeenCalled(); -}); diff --git a/src/plugins/home/public/application/components/welcome.tsx b/src/plugins/home/public/application/components/welcome.tsx index 1a6251ebdca11..9efa6d356d971 100644 --- a/src/plugins/home/public/application/components/welcome.tsx +++ b/src/plugins/home/public/application/components/welcome.tsx @@ -12,27 +12,17 @@ * in Elasticsearch. */ -import React, { Fragment } from 'react'; -import { - EuiLink, - EuiTextColor, - EuiTitle, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPortal, -} from '@elastic/eui'; +import React from 'react'; +import { EuiTitle, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPortal } from '@elastic/eui'; import { METRIC_TYPE } from '@kbn/analytics'; import { FormattedMessage } from '@kbn/i18n-react'; import { getServices } from '../kibana_services'; -import { TelemetryPluginStart } from '../../../../telemetry/public'; import { SampleDataCard } from './sample_data'; + interface Props { urlBasePath: string; onSkip: () => void; - telemetry?: TelemetryPluginStart; } /** @@ -47,7 +37,7 @@ export class Welcome extends React.Component { } }; - private redirecToAddData() { + private redirectToAddData() { this.services.application.navigateToApp('integrations', { path: '/browse' }); } @@ -58,68 +48,23 @@ export class Welcome extends React.Component { private onSampleDataConfirm = () => { this.services.trackUiMetric(METRIC_TYPE.CLICK, 'sampleDataConfirm'); - this.redirecToAddData(); + this.redirectToAddData(); }; componentDidMount() { - const { telemetry } = this.props; + const { welcomeService } = this.services; this.services.trackUiMetric(METRIC_TYPE.LOADED, 'welcomeScreenMount'); - if (telemetry?.telemetryService.userCanChangeSettings) { - telemetry.telemetryNotifications.setOptedInNoticeSeen(); - } document.addEventListener('keydown', this.hideOnEsc); + welcomeService.onRendered(); } componentWillUnmount() { document.removeEventListener('keydown', this.hideOnEsc); } - private renderTelemetryEnabledOrDisabledText = () => { - const { telemetry } = this.props; - if ( - !telemetry || - !telemetry.telemetryService.userCanChangeSettings || - !telemetry.telemetryService.getCanChangeOptInStatus() - ) { - return null; - } - - const isOptedIn = telemetry.telemetryService.getIsOptedIn(); - if (isOptedIn) { - return ( - - - - - - - ); - } else { - return ( - - - - - - - ); - } - }; - render() { - const { urlBasePath, telemetry } = this.props; + const { urlBasePath } = this.props; + const { welcomeService } = this.services; return (
@@ -146,28 +91,7 @@ export class Welcome extends React.Component { onDecline={this.onSampleDataDecline} /> - {!!telemetry && ( - - - - - - - {this.renderTelemetryEnabledOrDisabledText()} - - - - )} + {welcomeService.renderTelemetryNotice()}
diff --git a/src/plugins/home/public/application/kibana_services.ts b/src/plugins/home/public/application/kibana_services.ts index fdd325df96ac5..3ccfd9413a88a 100644 --- a/src/plugins/home/public/application/kibana_services.ts +++ b/src/plugins/home/public/application/kibana_services.ts @@ -17,7 +17,6 @@ import { ApplicationStart, } from 'kibana/public'; import { UiCounterMetricType } from '@kbn/analytics'; -import { TelemetryPluginStart } from '../../../telemetry/public'; import { UrlForwardingStart } from '../../../url_forwarding/public'; import { DataViewsContract } from '../../../data_views/public'; import { TutorialService } from '../services/tutorials'; @@ -26,6 +25,7 @@ import { FeatureCatalogueRegistry } from '../services/feature_catalogue'; import { EnvironmentService } from '../services/environment'; import { ConfigSchema } from '../../config'; import { SharePluginSetup } from '../../../share/public'; +import type { WelcomeService } from '../services/welcome'; export interface HomeKibanaServices { dataViewsService: DataViewsContract; @@ -46,9 +46,9 @@ export interface HomeKibanaServices { docLinks: DocLinksStart; addBasePath: (url: string) => string; environmentService: EnvironmentService; - telemetry?: TelemetryPluginStart; tutorialService: TutorialService; addDataService: AddDataService; + welcomeService: WelcomeService; } let services: HomeKibanaServices | null = null; diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts index 009382eee0009..3450f4f9d2caf 100644 --- a/src/plugins/home/public/index.ts +++ b/src/plugins/home/public/index.ts @@ -27,6 +27,8 @@ export type { TutorialVariables, TutorialDirectoryHeaderLinkComponent, TutorialModuleNoticeComponent, + WelcomeRenderTelemetryNotice, + WelcomeServiceSetup, } from './services'; export { INSTRUCTION_VARIANT, getDisplayText } from '../common/instruction_variant'; diff --git a/src/plugins/home/public/mocks.ts b/src/plugins/home/public/mocks.ts index 10c186ee3f4e3..42e489dea9d2a 100644 --- a/src/plugins/home/public/mocks.ts +++ b/src/plugins/home/public/mocks.ts @@ -8,16 +8,17 @@ import { featureCatalogueRegistryMock } from './services/feature_catalogue/feature_catalogue_registry.mock'; import { environmentServiceMock } from './services/environment/environment.mock'; -import { configSchema } from '../config'; import { tutorialServiceMock } from './services/tutorials/tutorial_service.mock'; import { addDataServiceMock } from './services/add_data/add_data_service.mock'; +import { HomePublicPluginSetup } from './plugin'; +import { welcomeServiceMock } from './services/welcome/welcome_service.mocks'; -const createSetupContract = () => ({ +const createSetupContract = (): jest.Mocked => ({ featureCatalogue: featureCatalogueRegistryMock.createSetup(), environment: environmentServiceMock.createSetup(), tutorials: tutorialServiceMock.createSetup(), addData: addDataServiceMock.createSetup(), - config: configSchema.validate({}), + welcomeScreen: welcomeServiceMock.createSetup(), }); export const homePluginMock = { diff --git a/src/plugins/home/public/plugin.test.mocks.ts b/src/plugins/home/public/plugin.test.mocks.ts index c3e3c50a2fe0f..22d314cbd6d06 100644 --- a/src/plugins/home/public/plugin.test.mocks.ts +++ b/src/plugins/home/public/plugin.test.mocks.ts @@ -10,14 +10,17 @@ import { featureCatalogueRegistryMock } from './services/feature_catalogue/featu import { environmentServiceMock } from './services/environment/environment.mock'; import { tutorialServiceMock } from './services/tutorials/tutorial_service.mock'; import { addDataServiceMock } from './services/add_data/add_data_service.mock'; +import { welcomeServiceMock } from './services/welcome/welcome_service.mocks'; export const registryMock = featureCatalogueRegistryMock.create(); export const environmentMock = environmentServiceMock.create(); export const tutorialMock = tutorialServiceMock.create(); export const addDataMock = addDataServiceMock.create(); +export const welcomeMock = welcomeServiceMock.create(); jest.doMock('./services', () => ({ FeatureCatalogueRegistry: jest.fn(() => registryMock), EnvironmentService: jest.fn(() => environmentMock), TutorialService: jest.fn(() => tutorialMock), AddDataService: jest.fn(() => addDataMock), + WelcomeService: jest.fn(() => welcomeMock), })); diff --git a/src/plugins/home/public/plugin.test.ts b/src/plugins/home/public/plugin.test.ts index 990f0dce54a05..57a1f5ec112aa 100644 --- a/src/plugins/home/public/plugin.test.ts +++ b/src/plugins/home/public/plugin.test.ts @@ -79,5 +79,18 @@ describe('HomePublicPlugin', () => { expect(setup).toHaveProperty('tutorials'); expect(setup.tutorials).toHaveProperty('setVariable'); }); + + test('wires up and returns welcome service', async () => { + const setup = await new HomePublicPlugin(mockInitializerContext).setup( + coreMock.createSetup() as any, + { + share: mockShare, + urlForwarding: urlForwardingPluginMock.createSetupContract(), + } + ); + expect(setup).toHaveProperty('welcomeScreen'); + expect(setup.welcomeScreen).toHaveProperty('registerOnRendered'); + expect(setup.welcomeScreen).toHaveProperty('registerTelemetryNoticeRenderer'); + }); }); }); diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index 1ece73e71f393..af43e56a1d75d 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -25,11 +25,12 @@ import { TutorialServiceSetup, AddDataService, AddDataServiceSetup, + WelcomeService, + WelcomeServiceSetup, } from './services'; import { ConfigSchema } from '../config'; import { setServices } from './application/kibana_services'; import { DataViewsPublicPluginStart } from '../../data_views/public'; -import { TelemetryPluginStart } from '../../telemetry/public'; import { UsageCollectionSetup } from '../../usage_collection/public'; import { UrlForwardingSetup, UrlForwardingStart } from '../../url_forwarding/public'; import { AppNavLinkStatus } from '../../../core/public'; @@ -38,7 +39,6 @@ import { SharePluginSetup } from '../../share/public'; export interface HomePluginStartDependencies { dataViews: DataViewsPublicPluginStart; - telemetry?: TelemetryPluginStart; urlForwarding: UrlForwardingStart; } @@ -61,6 +61,7 @@ export class HomePublicPlugin private readonly environmentService = new EnvironmentService(); private readonly tutorialService = new TutorialService(); private readonly addDataService = new AddDataService(); + private readonly welcomeService = new WelcomeService(); constructor(private readonly initializerContext: PluginInitializerContext) {} @@ -76,7 +77,7 @@ export class HomePublicPlugin const trackUiMetric = usageCollection ? usageCollection.reportUiCounter.bind(usageCollection, 'Kibana_home') : () => {}; - const [coreStart, { telemetry, dataViews, urlForwarding: urlForwardingStart }] = + const [coreStart, { dataViews, urlForwarding: urlForwardingStart }] = await core.getStartServices(); setServices({ share, @@ -89,7 +90,6 @@ export class HomePublicPlugin savedObjectsClient: coreStart.savedObjects.client, chrome: coreStart.chrome, application: coreStart.application, - telemetry, uiSettings: core.uiSettings, addBasePath: core.http.basePath.prepend, getBasePath: core.http.basePath.get, @@ -100,6 +100,7 @@ export class HomePublicPlugin tutorialService: this.tutorialService, addDataService: this.addDataService, featureCatalogue: this.featuresCatalogueRegistry, + welcomeService: this.welcomeService, }); coreStart.chrome.docTitle.change( i18n.translate('home.pageTitle', { defaultMessage: 'Home' }) @@ -132,6 +133,7 @@ export class HomePublicPlugin environment: { ...this.environmentService.setup() }, tutorials: { ...this.tutorialService.setup() }, addData: { ...this.addDataService.setup() }, + welcomeScreen: { ...this.welcomeService.setup() }, }; } @@ -159,12 +161,12 @@ export interface HomePublicPluginSetup { tutorials: TutorialServiceSetup; addData: AddDataServiceSetup; featureCatalogue: FeatureCatalogueSetup; + welcomeScreen: WelcomeServiceSetup; /** * The environment service is only available for a transition period and will * be replaced by display specific extension points. * @deprecated */ - environment: EnvironmentSetup; } diff --git a/src/plugins/home/public/services/index.ts b/src/plugins/home/public/services/index.ts index 2ee68a9eef0c2..41bc9ee258ceb 100644 --- a/src/plugins/home/public/services/index.ts +++ b/src/plugins/home/public/services/index.ts @@ -28,3 +28,6 @@ export type { export { AddDataService } from './add_data'; export type { AddDataServiceSetup, AddDataTab } from './add_data'; + +export { WelcomeService } from './welcome'; +export type { WelcomeServiceSetup, WelcomeRenderTelemetryNotice } from './welcome'; diff --git a/src/plugins/home/public/services/welcome/index.ts b/src/plugins/home/public/services/welcome/index.ts new file mode 100644 index 0000000000000..371c6044c5dc5 --- /dev/null +++ b/src/plugins/home/public/services/welcome/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { WelcomeServiceSetup, WelcomeRenderTelemetryNotice } from './welcome_service'; +export { WelcomeService } from './welcome_service'; diff --git a/src/plugins/home/public/services/welcome/welcome_service.mocks.ts b/src/plugins/home/public/services/welcome/welcome_service.mocks.ts new file mode 100644 index 0000000000000..921cb99066327 --- /dev/null +++ b/src/plugins/home/public/services/welcome/welcome_service.mocks.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PublicMethodsOf } from '@kbn/utility-types'; +import { WelcomeService, WelcomeServiceSetup } from './welcome_service'; + +const createSetupMock = (): jest.Mocked => { + const welcomeService = new WelcomeService(); + const welcomeServiceSetup = welcomeService.setup(); + return { + registerTelemetryNoticeRenderer: jest + .fn() + .mockImplementation(welcomeServiceSetup.registerTelemetryNoticeRenderer), + registerOnRendered: jest.fn().mockImplementation(welcomeServiceSetup.registerOnRendered), + }; +}; + +const createMock = (): jest.Mocked> => { + const welcomeService = new WelcomeService(); + + return { + setup: jest.fn().mockImplementation(welcomeService.setup), + onRendered: jest.fn().mockImplementation(welcomeService.onRendered), + renderTelemetryNotice: jest.fn().mockImplementation(welcomeService.renderTelemetryNotice), + }; +}; + +export const welcomeServiceMock = { + createSetup: createSetupMock, + create: createMock, +}; diff --git a/src/plugins/home/public/services/welcome/welcome_service.test.ts b/src/plugins/home/public/services/welcome/welcome_service.test.ts new file mode 100644 index 0000000000000..df2f95718c78b --- /dev/null +++ b/src/plugins/home/public/services/welcome/welcome_service.test.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { WelcomeService, WelcomeServiceSetup } from './welcome_service'; + +describe('WelcomeService', () => { + let welcomeService: WelcomeService; + let welcomeServiceSetup: WelcomeServiceSetup; + + beforeEach(() => { + welcomeService = new WelcomeService(); + welcomeServiceSetup = welcomeService.setup(); + }); + describe('onRendered', () => { + test('it should register an onRendered listener', () => { + const onRendered = jest.fn(); + welcomeServiceSetup.registerOnRendered(onRendered); + + welcomeService.onRendered(); + expect(onRendered).toHaveBeenCalledTimes(1); + }); + + test('it should handle onRendered errors', () => { + const onRendered = jest.fn().mockImplementation(() => { + throw new Error('Something went terribly wrong'); + }); + welcomeServiceSetup.registerOnRendered(onRendered); + + expect(() => welcomeService.onRendered()).not.toThrow(); + expect(onRendered).toHaveBeenCalledTimes(1); + }); + + test('it should allow registering multiple onRendered listeners', () => { + const onRendered = jest.fn(); + const onRendered2 = jest.fn(); + welcomeServiceSetup.registerOnRendered(onRendered); + welcomeServiceSetup.registerOnRendered(onRendered2); + + welcomeService.onRendered(); + expect(onRendered).toHaveBeenCalledTimes(1); + expect(onRendered2).toHaveBeenCalledTimes(1); + }); + + test('if the same handler is registered twice, it is called twice', () => { + const onRendered = jest.fn(); + welcomeServiceSetup.registerOnRendered(onRendered); + welcomeServiceSetup.registerOnRendered(onRendered); + + welcomeService.onRendered(); + expect(onRendered).toHaveBeenCalledTimes(2); + }); + }); + describe('renderTelemetryNotice', () => { + test('it should register a renderer', () => { + const renderer = jest.fn().mockReturnValue('rendered text'); + welcomeServiceSetup.registerTelemetryNoticeRenderer(renderer); + + expect(welcomeService.renderTelemetryNotice()).toEqual('rendered text'); + }); + + test('it should fail to register a 2nd renderer and still use the first registered renderer', () => { + const renderer = jest.fn().mockReturnValue('rendered text'); + const renderer2 = jest.fn().mockReturnValue('other text'); + welcomeServiceSetup.registerTelemetryNoticeRenderer(renderer); + expect(() => welcomeServiceSetup.registerTelemetryNoticeRenderer(renderer2)).toThrowError( + 'Only one renderTelemetryNotice handler can be registered' + ); + + expect(welcomeService.renderTelemetryNotice()).toEqual('rendered text'); + }); + + test('it should handle errors in the renderer', () => { + const renderer = jest.fn().mockImplementation(() => { + throw new Error('Something went terribly wrong'); + }); + welcomeServiceSetup.registerTelemetryNoticeRenderer(renderer); + + expect(welcomeService.renderTelemetryNotice()).toEqual(null); + }); + }); +}); diff --git a/src/plugins/home/public/services/welcome/welcome_service.ts b/src/plugins/home/public/services/welcome/welcome_service.ts new file mode 100644 index 0000000000000..46cf139adb36a --- /dev/null +++ b/src/plugins/home/public/services/welcome/welcome_service.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type WelcomeRenderTelemetryNotice = () => null | JSX.Element; + +export interface WelcomeServiceSetup { + /** + * Register listeners to be called when the Welcome component is mounted. + * It can be called multiple times to register multiple listeners. + */ + registerOnRendered: (onRendered: () => void) => void; + /** + * Register a renderer of the telemetry notice to be shown below the Welcome page. + */ + registerTelemetryNoticeRenderer: (renderTelemetryNotice: WelcomeRenderTelemetryNotice) => void; +} + +export class WelcomeService { + private readonly onRenderedHandlers: Array<() => void> = []; + private renderTelemetryNoticeHandler?: WelcomeRenderTelemetryNotice; + + public setup = (): WelcomeServiceSetup => { + return { + registerOnRendered: (onRendered) => { + this.onRenderedHandlers.push(onRendered); + }, + registerTelemetryNoticeRenderer: (renderTelemetryNotice) => { + if (this.renderTelemetryNoticeHandler) { + throw new Error('Only one renderTelemetryNotice handler can be registered'); + } + this.renderTelemetryNoticeHandler = renderTelemetryNotice; + }, + }; + }; + + public onRendered = () => { + this.onRenderedHandlers.forEach((onRendered) => { + try { + onRendered(); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + } + }); + }; + + public renderTelemetryNotice = () => { + if (this.renderTelemetryNoticeHandler) { + try { + return this.renderTelemetryNoticeHandler(); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + } + } + return null; + }; +} diff --git a/src/plugins/home/tsconfig.json b/src/plugins/home/tsconfig.json index fa98b98ff8e1c..17d0fc7bd91ac 100644 --- a/src/plugins/home/tsconfig.json +++ b/src/plugins/home/tsconfig.json @@ -15,7 +15,6 @@ { "path": "../kibana_react/tsconfig.json" }, { "path": "../share/tsconfig.json" }, { "path": "../url_forwarding/tsconfig.json" }, - { "path": "../usage_collection/tsconfig.json" }, - { "path": "../telemetry/tsconfig.json" } + { "path": "../usage_collection/tsconfig.json" } ] } diff --git a/src/plugins/telemetry/kibana.json b/src/plugins/telemetry/kibana.json index 09cc6accb68f4..a6796e42f9228 100644 --- a/src/plugins/telemetry/kibana.json +++ b/src/plugins/telemetry/kibana.json @@ -8,6 +8,7 @@ "server": true, "ui": true, "requiredPlugins": ["telemetryCollectionManager", "usageCollection", "screenshotMode"], + "optionalPlugins": ["home", "security"], "extraPublicDirs": ["common/constants"], "requiredBundles": ["kibanaUtils", "kibanaReact"] } diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index 3072ff67703d7..794183cb8a8f5 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -31,6 +31,8 @@ import { } from '../common/telemetry_config'; import { getNotifyUserAboutOptInDefault } from '../common/telemetry_config/get_telemetry_notify_user_about_optin_default'; import { PRIVACY_STATEMENT_URL } from '../common/constants'; +import { HomePublicPluginSetup } from '../../home/public'; +import { renderWelcomeTelemetryNotice } from './render_welcome_telemetry_notice'; /** * Publicly exposed APIs from the Telemetry Service @@ -82,6 +84,7 @@ export interface TelemetryPluginStart { interface TelemetryPluginSetupDependencies { screenshotMode: ScreenshotModePluginSetup; + home?: HomePublicPluginSetup; } /** @@ -121,7 +124,7 @@ export class TelemetryPlugin implements Plugin { + if (this.telemetryService?.userCanChangeSettings) { + this.telemetryNotifications?.setOptedInNoticeSeen(); + } + }); + + home.welcomeScreen.registerTelemetryNoticeRenderer(() => + renderWelcomeTelemetryNotice(this.telemetryService!, http.basePath.prepend) + ); + } + return { telemetryService: this.getTelemetryServicePublicApis(), }; diff --git a/src/plugins/telemetry/public/render_welcome_telemetry_notice.test.ts b/src/plugins/telemetry/public/render_welcome_telemetry_notice.test.ts new file mode 100644 index 0000000000000..6da76db915656 --- /dev/null +++ b/src/plugins/telemetry/public/render_welcome_telemetry_notice.test.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { renderWelcomeTelemetryNotice } from './render_welcome_telemetry_notice'; +import { mockTelemetryService } from './mocks'; + +describe('renderWelcomeTelemetryNotice', () => { + test('it should show the opt-out message', () => { + const telemetryService = mockTelemetryService(); + const component = mountWithIntl(renderWelcomeTelemetryNotice(telemetryService, (url) => url)); + expect(component.exists('[id="telemetry.dataManagementDisableCollectionLink"]')).toBe(true); + }); + + test('it should show the opt-in message', () => { + const telemetryService = mockTelemetryService({ config: { optIn: false } }); + const component = mountWithIntl(renderWelcomeTelemetryNotice(telemetryService, (url) => url)); + expect(component.exists('[id="telemetry.dataManagementEnableCollectionLink"]')).toBe(true); + }); + + test('it should not show opt-in/out options if user cannot change the settings', () => { + const telemetryService = mockTelemetryService({ config: { allowChangingOptInStatus: false } }); + const component = mountWithIntl(renderWelcomeTelemetryNotice(telemetryService, (url) => url)); + expect(component.exists('[id="telemetry.dataManagementDisableCollectionLink"]')).toBe(false); + expect(component.exists('[id="telemetry.dataManagementEnableCollectionLink"]')).toBe(false); + }); +}); diff --git a/src/plugins/telemetry/public/render_welcome_telemetry_notice.tsx b/src/plugins/telemetry/public/render_welcome_telemetry_notice.tsx new file mode 100644 index 0000000000000..8ef26fb797d53 --- /dev/null +++ b/src/plugins/telemetry/public/render_welcome_telemetry_notice.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiLink, EuiSpacer, EuiTextColor } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { TelemetryService } from './services'; +import { PRIVACY_STATEMENT_URL } from '../common/constants'; + +export function renderWelcomeTelemetryNotice( + telemetryService: TelemetryService, + addBasePath: (url: string) => string +) { + return ( + <> + + + + + + {renderTelemetryEnabledOrDisabledText(telemetryService, addBasePath)} + + + + ); +} + +function renderTelemetryEnabledOrDisabledText( + telemetryService: TelemetryService, + addBasePath: (url: string) => string +) { + if (!telemetryService.userCanChangeSettings || !telemetryService.getCanChangeOptInStatus()) { + return null; + } + + const isOptedIn = telemetryService.getIsOptedIn(); + + if (isOptedIn) { + return ( + <> + + + + + + ); + } else { + return ( + <> + + + + + + ); + } +} diff --git a/src/plugins/telemetry/server/plugin.ts b/src/plugins/telemetry/server/plugin.ts index 73c61ea1c5038..681a871ba105b 100644 --- a/src/plugins/telemetry/server/plugin.ts +++ b/src/plugins/telemetry/server/plugin.ts @@ -23,6 +23,7 @@ import type { Plugin, Logger, } from 'src/core/server'; +import type { SecurityPluginStart } from '../../../../x-pack/plugins/security/server'; import { SavedObjectsClient } from '../../../core/server'; import { registerRoutes } from './routes'; import { registerCollection } from './telemetry_collection'; @@ -42,6 +43,7 @@ interface TelemetryPluginsDepsSetup { interface TelemetryPluginsDepsStart { telemetryCollectionManager: TelemetryCollectionManagerPluginStart; + security?: SecurityPluginStart; } /** @@ -90,6 +92,8 @@ export class TelemetryPlugin implements Plugin(1); + private security?: SecurityPluginStart; + constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); this.isDev = initializerContext.env.mode.dev; @@ -119,6 +123,7 @@ export class TelemetryPlugin implements Plugin this.security, }); this.registerMappings((opts) => savedObjects.registerType(opts)); @@ -137,11 +142,17 @@ export class TelemetryPlugin implements Plugin; + getSecurity: SecurityGetter; } export function registerRoutes(options: RegisterRoutesParams) { - const { isDev, telemetryCollectionManager, router, savedObjectsInternalClient$ } = options; + const { isDev, telemetryCollectionManager, router, savedObjectsInternalClient$, getSecurity } = + options; registerTelemetryOptInRoutes(options); - registerTelemetryUsageStatsRoutes(router, telemetryCollectionManager, isDev); + registerTelemetryUsageStatsRoutes(router, telemetryCollectionManager, isDev, getSecurity); registerTelemetryOptInStatsRoutes(router, telemetryCollectionManager); registerTelemetryUserHasSeenNotice(router); registerTelemetryLastReported(router, savedObjectsInternalClient$); diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts index 2a95665662194..6139eee3e10ca 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts @@ -75,7 +75,6 @@ export function registerTelemetryOptInStatsRoutes( const statsGetterConfig: StatsGetterConfig = { unencrypted, - request: req, }; const optInStatus = await telemetryCollectionManager.getOptInStats( diff --git a/src/plugins/telemetry/server/routes/telemetry_usage_stats.test.ts b/src/plugins/telemetry/server/routes/telemetry_usage_stats.test.ts index 736367446d3c0..bc7569585c127 100644 --- a/src/plugins/telemetry/server/routes/telemetry_usage_stats.test.ts +++ b/src/plugins/telemetry/server/routes/telemetry_usage_stats.test.ts @@ -8,7 +8,8 @@ import { registerTelemetryUsageStatsRoutes } from './telemetry_usage_stats'; import { coreMock, httpServerMock } from 'src/core/server/mocks'; -import type { RequestHandlerContext, IRouter } from 'kibana/server'; +import type { RequestHandlerContext, IRouter } from 'src/core/server'; +import { securityMock } from '../../../../../x-pack/plugins/security/server/mocks'; import { telemetryCollectionManagerPluginMock } from '../../../telemetry_collection_manager/server/mocks'; async function runRequest( @@ -35,13 +36,18 @@ describe('registerTelemetryUsageStatsRoutes', () => { }; const telemetryCollectionManager = telemetryCollectionManagerPluginMock.createSetupContract(); const mockCoreSetup = coreMock.createSetup(); - const mockRouter = mockCoreSetup.http.createRouter(); const mockStats = [{ clusterUuid: 'text', stats: 'enc_str' }]; telemetryCollectionManager.getStats.mockResolvedValue(mockStats); + const getSecurity = jest.fn(); + + let mockRouter: IRouter; + beforeEach(() => { + mockRouter = mockCoreSetup.http.createRouter(); + }); describe('clusters/_stats POST route', () => { it('registers _stats POST route and accepts body configs', () => { - registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true); + registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true, getSecurity); expect(mockRouter.post).toBeCalledTimes(1); const [routeConfig, handler] = (mockRouter.post as jest.Mock).mock.calls[0]; expect(routeConfig.path).toMatchInlineSnapshot(`"/api/telemetry/v2/clusters/_stats"`); @@ -50,11 +56,10 @@ describe('registerTelemetryUsageStatsRoutes', () => { }); it('responds with encrypted stats with no cache refresh by default', async () => { - registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true); + registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true, getSecurity); - const { mockRequest, mockResponse } = await runRequest(mockRouter); + const { mockResponse } = await runRequest(mockRouter); expect(telemetryCollectionManager.getStats).toBeCalledWith({ - request: mockRequest, unencrypted: undefined, refreshCache: undefined, }); @@ -63,39 +68,99 @@ describe('registerTelemetryUsageStatsRoutes', () => { }); it('when unencrypted is set getStats is called with unencrypted and refreshCache', async () => { - registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true); + registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true, getSecurity); - const { mockRequest } = await runRequest(mockRouter, { unencrypted: true }); + await runRequest(mockRouter, { unencrypted: true }); expect(telemetryCollectionManager.getStats).toBeCalledWith({ - request: mockRequest, unencrypted: true, refreshCache: true, }); }); it('calls getStats with refreshCache when set in body', async () => { - registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true); - const { mockRequest } = await runRequest(mockRouter, { refreshCache: true }); + registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true, getSecurity); + await runRequest(mockRouter, { refreshCache: true }); expect(telemetryCollectionManager.getStats).toBeCalledWith({ - request: mockRequest, unencrypted: undefined, refreshCache: true, }); }); it('calls getStats with refreshCache:true even if set to false in body when unencrypted is set to true', async () => { - registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true); - const { mockRequest } = await runRequest(mockRouter, { + registerTelemetryUsageStatsRoutes(mockRouter, telemetryCollectionManager, true, getSecurity); + await runRequest(mockRouter, { refreshCache: false, unencrypted: true, }); expect(telemetryCollectionManager.getStats).toBeCalledWith({ - request: mockRequest, unencrypted: true, refreshCache: true, }); }); + it('returns 403 when the user does not have enough permissions to request unencrypted telemetry', async () => { + const getSecurityMock = jest.fn().mockImplementation(() => { + const securityStartMock = securityMock.createStart(); + securityStartMock.authz.checkPrivilegesWithRequest.mockReturnValue({ + globally: () => ({ hasAllRequested: false }), + }); + return securityStartMock; + }); + registerTelemetryUsageStatsRoutes( + mockRouter, + telemetryCollectionManager, + true, + getSecurityMock + ); + const { mockResponse } = await runRequest(mockRouter, { + refreshCache: false, + unencrypted: true, + }); + expect(mockResponse.forbidden).toBeCalled(); + }); + + it('returns 200 when the user has enough permissions to request unencrypted telemetry', async () => { + const getSecurityMock = jest.fn().mockImplementation(() => { + const securityStartMock = securityMock.createStart(); + securityStartMock.authz.checkPrivilegesWithRequest.mockReturnValue({ + globally: () => ({ hasAllRequested: true }), + }); + return securityStartMock; + }); + registerTelemetryUsageStatsRoutes( + mockRouter, + telemetryCollectionManager, + true, + getSecurityMock + ); + const { mockResponse } = await runRequest(mockRouter, { + refreshCache: false, + unencrypted: true, + }); + expect(mockResponse.ok).toBeCalled(); + }); + + it('returns 200 when the user does not have enough permissions to request unencrypted telemetry but it requests encrypted', async () => { + const getSecurityMock = jest.fn().mockImplementation(() => { + const securityStartMock = securityMock.createStart(); + securityStartMock.authz.checkPrivilegesWithRequest.mockReturnValue({ + globally: () => ({ hasAllRequested: false }), + }); + return securityStartMock; + }); + registerTelemetryUsageStatsRoutes( + mockRouter, + telemetryCollectionManager, + true, + getSecurityMock + ); + const { mockResponse } = await runRequest(mockRouter, { + refreshCache: false, + unencrypted: false, + }); + expect(mockResponse.ok).toBeCalled(); + }); + it.todo('always returns an empty array on errors on encrypted payload'); it.todo('returns the actual request error object when in development mode'); it.todo('returns forbidden on unencrypted and ES returns 403 in getStats'); diff --git a/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts b/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts index 2f72ae818f112..4647f5afe0760 100644 --- a/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts +++ b/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts @@ -12,11 +12,15 @@ import { TelemetryCollectionManagerPluginSetup, StatsGetterConfig, } from 'src/plugins/telemetry_collection_manager/server'; +import type { SecurityPluginStart } from '../../../../../x-pack/plugins/security/server'; + +export type SecurityGetter = () => SecurityPluginStart | undefined; export function registerTelemetryUsageStatsRoutes( router: IRouter, telemetryCollectionManager: TelemetryCollectionManagerPluginSetup, - isDev: boolean + isDev: boolean, + getSecurity: SecurityGetter ) { router.post( { @@ -31,9 +35,22 @@ export function registerTelemetryUsageStatsRoutes( async (context, req, res) => { const { unencrypted, refreshCache } = req.body; + const security = getSecurity(); + if (security && unencrypted) { + // Normally we would use `options: { tags: ['access:decryptedTelemetry'] }` in the route definition to check authorization for an + // API action, however, we want to check this conditionally based on the `unencrypted` parameter. In this case we need to use the + // security API directly to check privileges for this action. Note that the 'decryptedTelemetry' API privilege string is only + // granted to users that have "Global All" or "Global Read" privileges in Kibana. + const { checkPrivilegesWithRequest, actions } = security.authz; + const privileges = { kibana: actions.api.get('decryptedTelemetry') }; + const { hasAllRequested } = await checkPrivilegesWithRequest(req).globally(privileges); + if (!hasAllRequested) { + return res.forbidden(); + } + } + try { const statsConfig: StatsGetterConfig = { - request: req, unencrypted, refreshCache: unencrypted || refreshCache, }; diff --git a/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts b/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts index 83f33a894b903..4340eaafd2d8f 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts @@ -7,10 +7,9 @@ */ import { omit } from 'lodash'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; -import { StatsCollectionContext } from 'src/plugins/telemetry_collection_manager/server'; -import { ElasticsearchClient } from 'src/core/server'; +import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; +import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import type { StatsCollectionContext } from 'src/plugins/telemetry_collection_manager/server'; export interface KibanaUsageStats { kibana: { @@ -71,9 +70,8 @@ export function handleKibanaStats( export async function getKibana( usageCollection: UsageCollectionSetup, asInternalUser: ElasticsearchClient, - soClient: SavedObjectsClientContract, - kibanaRequest: KibanaRequest | undefined // intentionally `| undefined` to enforce providing the parameter + soClient: SavedObjectsClientContract ): Promise { - const usage = await usageCollection.bulkFetch(asInternalUser, soClient, kibanaRequest); + const usage = await usageCollection.bulkFetch(asInternalUser, soClient); return usageCollection.toObject(usage); } diff --git a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts index 2392ac570ecbc..fa45438e00fbe 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts @@ -14,7 +14,7 @@ import { usageCollectionPluginMock, createCollectorFetchContextMock, } from '../../../usage_collection/server/mocks'; -import { elasticsearchServiceMock, httpServerMock } from '../../../../../src/core/server/mocks'; +import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; import { StatsCollectionConfig } from '../../../telemetry_collection_manager/server'; function mockUsageCollection(kibanaUsage = {}) { @@ -74,7 +74,6 @@ function mockStatsCollectionConfig( ...createCollectorFetchContextMock(), esClient: mockGetLocalStats(clusterInfo, clusterStats), usageCollection: mockUsageCollection(kibana), - kibanaRequest: httpServerMock.createKibanaRequest(), refreshCache: false, }; } diff --git a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts index ae2a849ccfa19..73de59ae8156a 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts @@ -65,7 +65,7 @@ export const getLocalStats: StatsGetter = async ( config, context ) => { - const { usageCollection, esClient, soClient, kibanaRequest } = config; + const { usageCollection, esClient, soClient } = config; return await Promise.all( clustersDetails.map(async (clustersDetail) => { @@ -73,7 +73,7 @@ export const getLocalStats: StatsGetter = async ( getClusterInfo(esClient), // cluster info getClusterStats(esClient), // cluster stats (not to be confused with cluster _state_) getNodesUsage(esClient), // nodes_usage info - getKibana(usageCollection, esClient, soClient, kibanaRequest), + getKibana(usageCollection, esClient, soClient), getDataTelemetry(esClient), ]); return handleLocalStats( diff --git a/src/plugins/telemetry/tsconfig.json b/src/plugins/telemetry/tsconfig.json index d50ccd563fe5a..052d484447e42 100644 --- a/src/plugins/telemetry/tsconfig.json +++ b/src/plugins/telemetry/tsconfig.json @@ -17,10 +17,12 @@ ], "references": [ { "path": "../../core/tsconfig.json" }, + { "path": "../../plugins/home/tsconfig.json" }, { "path": "../../plugins/kibana_react/tsconfig.json" }, { "path": "../../plugins/kibana_utils/tsconfig.json" }, { "path": "../../plugins/screenshot_mode/tsconfig.json" }, { "path": "../../plugins/telemetry_collection_manager/tsconfig.json" }, - { "path": "../../plugins/usage_collection/tsconfig.json" } + { "path": "../../plugins/usage_collection/tsconfig.json" }, + { "path": "../../../x-pack/plugins/security/tsconfig.json" } ] } diff --git a/src/plugins/telemetry_collection_manager/server/plugin.test.ts b/src/plugins/telemetry_collection_manager/server/plugin.test.ts index ca932e92d98bd..990e237b6b272 100644 --- a/src/plugins/telemetry_collection_manager/server/plugin.test.ts +++ b/src/plugins/telemetry_collection_manager/server/plugin.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { coreMock, httpServerMock } from '../../../core/server/mocks'; +import { coreMock } from '../../../core/server/mocks'; import { usageCollectionPluginMock } from '../../usage_collection/server/mocks'; import { TelemetryCollectionManagerPlugin } from './plugin'; import type { BasicStatsPayload, CollectionStrategyConfig, StatsGetterConfig } from './types'; @@ -217,19 +217,17 @@ describe('Telemetry Collection Manager', () => { }); }); describe('unencrypted: true', () => { - const mockRequest = httpServerMock.createKibanaRequest(); const config: StatsGetterConfig = { unencrypted: true, - request: mockRequest, }; describe('getStats', () => { - test('getStats returns empty because clusterDetails returns empty, and the soClient is not an instance of the TelemetrySavedObjectsClient', async () => { + test('getStats returns empty because clusterDetails returns empty, and the soClient is an instance of the TelemetrySavedObjectsClient', async () => { collectionStrategy.clusterDetailsGetter.mockResolvedValue([]); await expect(setupApi.getStats(config)).resolves.toStrictEqual([]); expect( collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient - ).not.toBeInstanceOf(TelemetrySavedObjectsClient); + ).toBeInstanceOf(TelemetrySavedObjectsClient); }); test('returns encrypted payload (assumes opted-in when no explicitly opted-out)', async () => { collectionStrategy.clusterDetailsGetter.mockResolvedValue([ @@ -249,7 +247,7 @@ describe('Telemetry Collection Manager', () => { expect( collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient - ).not.toBeInstanceOf(TelemetrySavedObjectsClient); + ).toBeInstanceOf(TelemetrySavedObjectsClient); }); it('calls getStats with config { refreshCache: true } even if set to false', async () => { @@ -267,7 +265,6 @@ describe('Telemetry Collection Manager', () => { expect(getStatsCollectionConfig).toReturnWith( expect.objectContaining({ refreshCache: true, - kibanaRequest: mockRequest, }) ); @@ -281,7 +278,7 @@ describe('Telemetry Collection Manager', () => { await expect(setupApi.getOptInStats(true, config)).resolves.toStrictEqual([]); expect( collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient - ).not.toBeInstanceOf(TelemetrySavedObjectsClient); + ).toBeInstanceOf(TelemetrySavedObjectsClient); }); test('returns results for opt-in true', async () => { @@ -296,7 +293,7 @@ describe('Telemetry Collection Manager', () => { ]); expect( collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient - ).not.toBeInstanceOf(TelemetrySavedObjectsClient); + ).toBeInstanceOf(TelemetrySavedObjectsClient); }); test('returns results for opt-in false', async () => { @@ -311,7 +308,7 @@ describe('Telemetry Collection Manager', () => { ]); expect( collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient - ).not.toBeInstanceOf(TelemetrySavedObjectsClient); + ).toBeInstanceOf(TelemetrySavedObjectsClient); }); }); }); diff --git a/src/plugins/telemetry_collection_manager/server/plugin.ts b/src/plugins/telemetry_collection_manager/server/plugin.ts index fad51ca1dbfde..cffe736f8eeaf 100644 --- a/src/plugins/telemetry_collection_manager/server/plugin.ts +++ b/src/plugins/telemetry_collection_manager/server/plugin.ts @@ -126,11 +126,10 @@ export class TelemetryCollectionManagerPlugin const esClient = this.getElasticsearchClient(config); const soClient = this.getSavedObjectsClient(config); // Provide the kibanaRequest so opted-in plugins can scope their custom clients only if the request is not encrypted - const kibanaRequest = config.unencrypted ? config.request : void 0; const refreshCache = config.unencrypted ? true : !!config.refreshCache; if (esClient && soClient) { - return { usageCollection, esClient, soClient, kibanaRequest, refreshCache }; + return { usageCollection, esClient, soClient, refreshCache }; } } @@ -142,9 +141,7 @@ export class TelemetryCollectionManagerPlugin * @private */ private getElasticsearchClient(config: StatsGetterConfig): ElasticsearchClient | undefined { - return config.unencrypted - ? this.elasticsearchClient?.asScoped(config.request).asCurrentUser - : this.elasticsearchClient?.asInternalUser; + return this.elasticsearchClient?.asInternalUser; } /** @@ -155,11 +152,7 @@ export class TelemetryCollectionManagerPlugin * @private */ private getSavedObjectsClient(config: StatsGetterConfig): SavedObjectsClientContract | undefined { - if (config.unencrypted) { - // Intentionally using the scoped client here to make use of all the security wrappers. - // It also returns spaces-scoped telemetry. - return this.savedObjectsService?.getScopedClient(config.request); - } else if (this.savedObjectsService) { + if (this.savedObjectsService) { // Wrapping the internalRepository with the `TelemetrySavedObjectsClient` // to ensure some best practices when collecting "all the telemetry" // (i.e.: `.find` requests should query all spaces) diff --git a/src/plugins/telemetry_collection_manager/server/types.ts b/src/plugins/telemetry_collection_manager/server/types.ts index 7ea32844a858c..9658c0d68d05d 100644 --- a/src/plugins/telemetry_collection_manager/server/types.ts +++ b/src/plugins/telemetry_collection_manager/server/types.ts @@ -6,14 +6,9 @@ * Side Public License, v 1. */ -import { - ElasticsearchClient, - Logger, - KibanaRequest, - SavedObjectsClientContract, -} from 'src/core/server'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { TelemetryCollectionManagerPlugin } from './plugin'; +import type { ElasticsearchClient, Logger, SavedObjectsClientContract } from 'src/core/server'; +import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import type { TelemetryCollectionManagerPlugin } from './plugin'; export interface TelemetryCollectionManagerPluginSetup { setCollectionStrategy: ( @@ -36,7 +31,6 @@ export interface TelemetryOptInStats { export interface BaseStatsGetterConfig { unencrypted: boolean; refreshCache?: boolean; - request?: KibanaRequest; } export interface EncryptedStatsGetterConfig extends BaseStatsGetterConfig { @@ -45,7 +39,6 @@ export interface EncryptedStatsGetterConfig extends BaseStatsGetterConfig { export interface UnencryptedStatsGetterConfig extends BaseStatsGetterConfig { unencrypted: true; - request: KibanaRequest; } export interface ClusterDetails { @@ -56,7 +49,6 @@ export interface StatsCollectionConfig { usageCollection: UsageCollectionSetup; esClient: ElasticsearchClient; soClient: SavedObjectsClientContract; - kibanaRequest: KibanaRequest | undefined; // intentionally `| undefined` to enforce providing the parameter refreshCache: boolean; } diff --git a/src/plugins/usage_collection/README.mdx b/src/plugins/usage_collection/README.mdx index a58f197818bf4..03d8f7badb8c2 100644 --- a/src/plugins/usage_collection/README.mdx +++ b/src/plugins/usage_collection/README.mdx @@ -297,8 +297,7 @@ Some background: - `isReady` (added in v7.2.0 and v6.8.4) is a way for a usage collector to announce that some async process must finish first before it can return data in the `fetch` method (e.g. a client needs to ne initialized, or the task manager needs to run a task first). If any collector reports that it is not ready when we call its `fetch` method, we reset a flag to try again and, after a set amount of time, collect data from those collectors that are ready and skip any that are not. This means that if a collector returns `true` for `isReady` and it actually isn't ready to return data, there won't be telemetry data from that collector in that telemetry report (usually once per day). You should consider what it means if your collector doesn't return data in the first few documents when Kibana starts or, if we should wait for any other reason (e.g. the task manager needs to run your task first). If you need to tell telemetry collection to wait, you should implement this function with custom logic. If your `fetch` method can run without the need of any previous dependencies, then you can return true for `isReady` as shown in the example below. -- The `fetch` method needs to support multiple contexts in which it is called. For example, when a user requests the example of what we collect in the **Kibana>Advanced Settings>Usage data** section, the clients provided in the context of the function (`CollectorFetchContext`) are scoped to that user's privileges. The reason is to avoid exposing via telemetry any data that user should not have access to (i.e.: if the user does not have access to certain indices, they shouldn't be allowed to see the number of documents that exists in it). In this case, the `fetch` method receives the clients `esClient` and `soClient` scoped to the user who performed the HTTP API request. Alternatively, when requesting the usage data to be reported to the Remote Telemetry Service, the clients are scoped to the internal Kibana user (`kibana_system`). Please, mind it might have lower-level access than the default super-admin `elastic` test user. -In some scenarios, your collector might need to maintain its own client. An example of that is the `monitoring` plugin, that maintains a connection to the Remote Monitoring Cluster to push its monitoring data. If that's the case, your plugin can opt-in to receive the additional `kibanaRequest` parameter by adding `extendFetchContext.kibanaRequest: true` to the collector's config: it will be appended to the context of the `fetch` method only if the request needs to be scoped to a user other than Kibana Internal, so beware that your collector will need to work for both scenarios (especially for the scenario when `kibanaRequest` is missing). +- The clients provided to the `fetch` method are scoped to the internal Kibana user (`kibana_system`). Note: there will be many cases where you won't need to use the `esClient` or `soClient` function that gets passed in to your `fetch` method at all. Your feature might have an accumulating value in server memory, or read something from the OS. diff --git a/src/plugins/usage_collection/server/collector/collector.ts b/src/plugins/usage_collection/server/collector/collector.ts index 74373d44a359b..1ff04cf3650c0 100644 --- a/src/plugins/usage_collection/server/collector/collector.ts +++ b/src/plugins/usage_collection/server/collector/collector.ts @@ -7,20 +7,14 @@ */ import type { Logger } from 'src/core/server'; -import type { - CollectorFetchMethod, - CollectorOptions, - CollectorOptionsFetchExtendedContext, - ICollector, -} from './types'; +import type { CollectorFetchMethod, CollectorOptions, ICollector } from './types'; export class Collector implements ICollector { - public readonly extendFetchContext: CollectorOptionsFetchExtendedContext; - public readonly type: CollectorOptions['type']; - public readonly fetch: CollectorFetchMethod; - public readonly isReady: CollectorOptions['isReady']; + public readonly type: CollectorOptions['type']; + public readonly fetch: CollectorFetchMethod; + public readonly isReady: CollectorOptions['isReady']; /** * @private Constructor of a Collector. It should be called via the CollectorSet factory methods: `makeStatsCollector` and `makeUsageCollector` * @param log {@link Logger} @@ -28,15 +22,7 @@ export class Collector */ constructor( public readonly log: Logger, - { - type, - fetch, - isReady, - extendFetchContext = {}, - ...options - }: // Any does not affect here, but needs to be set so it doesn't affect anything else down the line - // eslint-disable-next-line @typescript-eslint/no-explicit-any - CollectorOptions + { type, fetch, isReady, ...options }: CollectorOptions ) { if (type === undefined) { throw new Error('Collector must be instantiated with a options.type string property'); @@ -50,6 +36,5 @@ export class Collector this.type = type; this.fetch = fetch; this.isReady = typeof isReady === 'function' ? isReady : () => true; - this.extendFetchContext = extendFetchContext; } } diff --git a/src/plugins/usage_collection/server/collector/collector_set.test.ts b/src/plugins/usage_collection/server/collector/collector_set.test.ts index 5e0698b286f79..87e841f3de4c5 100644 --- a/src/plugins/usage_collection/server/collector/collector_set.test.ts +++ b/src/plugins/usage_collection/server/collector/collector_set.test.ts @@ -15,7 +15,6 @@ import { elasticsearchServiceMock, loggingSystemMock, savedObjectsClientMock, - httpServerMock, executionContextServiceMock, } from '../../../../core/server/mocks'; import type { ExecutionContextSetup, Logger } from 'src/core/server'; @@ -39,7 +38,6 @@ describe('CollectorSet', () => { }); const mockEsClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const mockSoClient = savedObjectsClientMock.create(); - const req = void 0; // No need to instantiate any KibanaRequest in these tests it('should throw an error if non-Collector type of object is registered', () => { const collectors = new CollectorSet(collectorSetConfig); @@ -88,7 +86,7 @@ describe('CollectorSet', () => { }) ); - const result = await collectors.bulkFetch(mockEsClient, mockSoClient, req); + const result = await collectors.bulkFetch(mockEsClient, mockSoClient); expect(logger.debug).toHaveBeenCalledTimes(2); expect(logger.debug).toHaveBeenCalledWith('Getting ready collectors'); expect(logger.debug).toHaveBeenCalledWith('Fetching data from MY_TEST_COLLECTOR collector'); @@ -121,7 +119,7 @@ describe('CollectorSet', () => { let result; try { - result = await collectors.bulkFetch(mockEsClient, mockSoClient, req); + result = await collectors.bulkFetch(mockEsClient, mockSoClient); } catch (err) { // Do nothing } @@ -150,7 +148,7 @@ describe('CollectorSet', () => { }) ); - const result = await collectors.bulkFetch(mockEsClient, mockSoClient, req); + const result = await collectors.bulkFetch(mockEsClient, mockSoClient); expect(result).toStrictEqual([ { type: 'MY_TEST_COLLECTOR', @@ -178,7 +176,7 @@ describe('CollectorSet', () => { }) ); - const result = await collectors.bulkFetch(mockEsClient, mockSoClient, req); + const result = await collectors.bulkFetch(mockEsClient, mockSoClient); expect(result).toStrictEqual([ { type: 'MY_TEST_COLLECTOR', @@ -269,50 +267,6 @@ describe('CollectorSet', () => { collectorSet = new CollectorSet(collectorSetConfig); }); - test('TS should hide kibanaRequest when not opted-in', () => { - collectorSet.makeStatsCollector({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - }); - }); - - test('TS should hide kibanaRequest when not opted-in (explicit false)', () => { - collectorSet.makeStatsCollector({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - kibanaRequest: false, - }, - }); - }); - - test('TS should allow using kibanaRequest when opted-in (explicit true)', () => { - collectorSet.makeStatsCollector({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - kibanaRequest: true, - }, - }); - }); - test('fetch can use the logger (TS allows it)', () => { const collector = collectorSet.makeStatsCollector({ type: 'MY_TEST_COLLECTOR', @@ -339,188 +293,6 @@ describe('CollectorSet', () => { collectorSet = new CollectorSet(collectorSetConfig); }); - describe('TS validations', () => { - describe('when types are inferred', () => { - test('TS should hide kibanaRequest when not opted-in', () => { - collectorSet.makeUsageCollector({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - }); - }); - - test('TS should hide kibanaRequest when not opted-in (explicit false)', () => { - collectorSet.makeUsageCollector({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - kibanaRequest: false, - }, - }); - }); - - test('TS should allow using kibanaRequest when opted-in (explicit true)', () => { - collectorSet.makeUsageCollector({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - kibanaRequest: true, - }, - }); - }); - }); - - describe('when types are explicit', () => { - test('TS should hide `kibanaRequest` from ctx when undefined or false', () => { - collectorSet.makeUsageCollector<{ test: number }>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - }); - collectorSet.makeUsageCollector<{ test: number }, false>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - kibanaRequest: false, - }, - }); - collectorSet.makeUsageCollector<{ test: number }, false>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - }); - }); - test('TS should not allow `true` when types declare false', () => { - // false is the default when at least 1 type is specified - collectorSet.makeUsageCollector<{ test: number }>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - // @ts-expect-error - kibanaRequest: true, - }, - }); - collectorSet.makeUsageCollector<{ test: number }, false>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - // @ts-expect-error - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - // @ts-expect-error - kibanaRequest: true, - }, - }); - }); - - test('TS should allow `true` when types explicitly declare `true` and do not allow `false` or undefined', () => { - // false is the default when at least 1 type is specified - collectorSet.makeUsageCollector<{ test: number }, true>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - kibanaRequest: true, - }, - }); - collectorSet.makeUsageCollector<{ test: number }, true>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - // @ts-expect-error - kibanaRequest: false, - }, - }); - collectorSet.makeUsageCollector<{ test: number }, true>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - extendFetchContext: { - // @ts-expect-error - kibanaRequest: undefined, - }, - }); - collectorSet.makeUsageCollector<{ test: number }, true>({ - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - // @ts-expect-error - extendFetchContext: {}, - }); - collectorSet.makeUsageCollector<{ test: number }, true>( - // @ts-expect-error - { - type: 'MY_TEST_COLLECTOR', - isReady: () => true, - schema: { test: { type: 'long' } }, - fetch: (ctx) => { - const { kibanaRequest } = ctx; - return { test: kibanaRequest ? 1 : 0 }; - }, - } - ); - }); - }); - }); - test('fetch can use the logger (TS allows it)', () => { const collector = collectorSet.makeUsageCollector({ type: 'MY_TEST_COLLECTOR', @@ -777,31 +549,5 @@ describe('CollectorSet', () => { expect.any(Function) ); }); - - it('adds extra context to collectors with extendFetchContext config', async () => { - const mockReadyFetch = jest.fn().mockResolvedValue({}); - collectorSet.registerCollector( - collectorSet.makeUsageCollector({ - type: 'ready_col', - isReady: () => true, - schema: {}, - fetch: mockReadyFetch, - extendFetchContext: { kibanaRequest: true }, - }) - ); - - const mockEsClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - const mockSoClient = savedObjectsClientMock.create(); - const request = httpServerMock.createKibanaRequest(); - const results = await collectorSet.bulkFetch(mockEsClient, mockSoClient, request); - - expect(mockReadyFetch).toBeCalledTimes(1); - expect(mockReadyFetch).toBeCalledWith({ - esClient: mockEsClient, - soClient: mockSoClient, - kibanaRequest: request, - }); - expect(results).toHaveLength(2); - }); }); }); diff --git a/src/plugins/usage_collection/server/collector/collector_set.ts b/src/plugins/usage_collection/server/collector/collector_set.ts index 49332b0a1826f..3a7c0a66ac60d 100644 --- a/src/plugins/usage_collection/server/collector/collector_set.ts +++ b/src/plugins/usage_collection/server/collector/collector_set.ts @@ -11,7 +11,6 @@ import type { Logger, ElasticsearchClient, SavedObjectsClientContract, - KibanaRequest, KibanaExecutionContext, ExecutionContextSetup, } from 'src/core/server'; @@ -64,12 +63,8 @@ export class CollectorSet { * Instantiates a stats collector with the definition provided in the options * @param options Definition of the collector {@link CollectorOptions} */ - public makeStatsCollector = < - TFetchReturn, - WithKibanaRequest extends boolean, - ExtraOptions extends object = {} - >( - options: CollectorOptions + public makeStatsCollector = ( + options: CollectorOptions ) => { return new Collector(this.logger, options); }; @@ -78,15 +73,8 @@ export class CollectorSet { * Instantiates an usage collector with the definition provided in the options * @param options Definition of the collector {@link CollectorOptions} */ - public makeUsageCollector = < - TFetchReturn, - // TODO: Right now, users will need to explicitly claim `true` for TS to allow `kibanaRequest` usage. - // If we improve `telemetry-check-tools` so plugins do not need to specify TFetchReturn, - // we'll be able to remove the type defaults and TS will successfully infer the config value as provided in JS. - WithKibanaRequest extends boolean = false, - ExtraOptions extends object = {} - >( - options: UsageCollectorOptions + public makeUsageCollector = ( + options: UsageCollectorOptions ) => { return new UsageCollector(this.logger, options); }; @@ -191,7 +179,6 @@ export class CollectorSet { public bulkFetch = async ( esClient: ElasticsearchClient, soClient: SavedObjectsClientContract, - kibanaRequest: KibanaRequest | undefined, // intentionally `| undefined` to enforce providing the parameter collectors: Map = this.collectors ) => { this.logger.debug(`Getting ready collectors`); @@ -209,11 +196,7 @@ export class CollectorSet { readyCollectors.map(async (collector) => { this.logger.debug(`Fetching data from ${collector.type} collector`); try { - const context = { - esClient, - soClient, - ...(collector.extendFetchContext.kibanaRequest && { kibanaRequest }), - }; + const context = { esClient, soClient }; const executionContext: KibanaExecutionContext = { type: 'usage_collection', name: 'collector.fetch', @@ -254,16 +237,10 @@ export class CollectorSet { public bulkFetchUsage = async ( esClient: ElasticsearchClient, - savedObjectsClient: SavedObjectsClientContract, - kibanaRequest: KibanaRequest | undefined // intentionally `| undefined` to enforce providing the parameter + savedObjectsClient: SavedObjectsClientContract ) => { const usageCollectors = this.getFilteredCollectorSet((c) => c instanceof UsageCollector); - return await this.bulkFetch( - esClient, - savedObjectsClient, - kibanaRequest, - usageCollectors.collectors - ); + return await this.bulkFetch(esClient, savedObjectsClient, usageCollectors.collectors); }; /** diff --git a/src/plugins/usage_collection/server/collector/index.ts b/src/plugins/usage_collection/server/collector/index.ts index ca240a520ee24..e284844b34c34 100644 --- a/src/plugins/usage_collection/server/collector/index.ts +++ b/src/plugins/usage_collection/server/collector/index.ts @@ -17,7 +17,6 @@ export type { CollectorOptions, CollectorFetchContext, CollectorFetchMethod, - CollectorOptionsFetchExtendedContext, ICollector as Collector, } from './types'; export type { UsageCollectorOptions } from './usage_collector'; diff --git a/src/plugins/usage_collection/server/collector/types.ts b/src/plugins/usage_collection/server/collector/types.ts index bf1e9f4644b1b..8d427d211a191 100644 --- a/src/plugins/usage_collection/server/collector/types.ts +++ b/src/plugins/usage_collection/server/collector/types.ts @@ -6,12 +6,7 @@ * Side Public License, v 1. */ -import type { - ElasticsearchClient, - KibanaRequest, - SavedObjectsClientContract, - Logger, -} from 'src/core/server'; +import type { ElasticsearchClient, SavedObjectsClientContract, Logger } from 'src/core/server'; /** Types matching number values **/ export type AllowedSchemaNumberTypes = @@ -73,7 +68,7 @@ export type MakeSchemaFrom = { * * @remark Bear in mind when testing your collector that your user has the same privileges as the Kibana Internal user to ensure the expected data is sent to the remote cluster. */ -export type CollectorFetchContext = { +export interface CollectorFetchContext { /** * Request-scoped Elasticsearch client * @remark Bear in mind when testing your collector that your user has the same privileges as the Kibana Internal user to ensure the expected data is sent to the remote cluster (more info: {@link CollectorFetchContext}) @@ -84,58 +79,22 @@ export type CollectorFetchContext = ( +export type CollectorFetchMethod = ( this: ICollector & ExtraOptions, // Specify the context of `this` for this.log and others to become available - context: CollectorFetchContext + context: CollectorFetchContext ) => Promise | TReturn; -export interface ICollectorOptionsFetchExtendedContext { - /** - * Set to `true` if your `fetch` method requires the `KibanaRequest` object to be added in its context {@link CollectorFetchContextWithRequest}. - * @remark You should fully acknowledge that by using the `KibanaRequest` in your collector, you need to ensure it should specially work without it because it won't be provided when building the telemetry payload actually sent to the remote telemetry service. - */ - kibanaRequest?: WithKibanaRequest; -} - -/** - * The options to extend the context provided to the `fetch` method. - * @remark Only to be used in very rare scenarios when this is really needed. - */ -export type CollectorOptionsFetchExtendedContext = - ICollectorOptionsFetchExtendedContext & - (WithKibanaRequest extends true // If enforced to true via Types, the config must be expected - ? Required, 'kibanaRequest'>> - : {}); - /** * Options to instantiate a collector */ -export type CollectorOptions< - TFetchReturn = unknown, - WithKibanaRequest extends boolean = boolean, - ExtraOptions extends object = {} -> = { +export type CollectorOptions = { /** * Unique string identifier for the collector */ @@ -152,17 +111,8 @@ export type CollectorOptions< * The method that will collect and return the data in the final format. * @param collectorFetchContext {@link CollectorFetchContext} */ - fetch: CollectorFetchMethod; -} & ExtraOptions & - (WithKibanaRequest extends true // If enforced to true via Types, the config must be enforced - ? { - /** {@link CollectorOptionsFetchExtendedContext} **/ - extendFetchContext: CollectorOptionsFetchExtendedContext; - } - : { - /** {@link CollectorOptionsFetchExtendedContext} **/ - extendFetchContext?: CollectorOptionsFetchExtendedContext; - }); + fetch: CollectorFetchMethod; +} & ExtraOptions; /** * Common interface for Usage and Stats Collectors @@ -170,13 +120,8 @@ export type CollectorOptions< export interface ICollector { /** Logger **/ readonly log: Logger; - /** - * The options to extend the context provided to the `fetch` method: {@link CollectorOptionsFetchExtendedContext}. - * @remark Only to be used in very rare scenarios when this is really needed. - */ - readonly extendFetchContext: CollectorOptionsFetchExtendedContext; /** The registered type (aka name) of the collector **/ - readonly type: CollectorOptions['type']; + readonly type: CollectorOptions['type']; /** * The actual logic that reports the Usage collection. * It will be called on every collection request. @@ -188,9 +133,9 @@ export interface ICollector { * [type]: await fetch(context) * } */ - readonly fetch: CollectorFetchMethod; + readonly fetch: CollectorFetchMethod; /** * Should return `true` when it's safe to call the `fetch` method. */ - readonly isReady: CollectorOptions['isReady']; + readonly isReady: CollectorOptions['isReady']; } diff --git a/src/plugins/usage_collection/server/collector/usage_collector.ts b/src/plugins/usage_collection/server/collector/usage_collector.ts index 15f7cd9c627fc..2ed8c2a50dbaf 100644 --- a/src/plugins/usage_collection/server/collector/usage_collector.ts +++ b/src/plugins/usage_collection/server/collector/usage_collector.ts @@ -15,10 +15,9 @@ import { Collector } from './collector'; */ export type UsageCollectorOptions< TFetchReturn = unknown, - WithKibanaRequest extends boolean = false, ExtraOptions extends object = {} -> = CollectorOptions & - Required, 'schema'>>; +> = CollectorOptions & + Required, 'schema'>>; /** * @private Only used in fixtures as a type @@ -27,12 +26,7 @@ export class UsageCollector exte TFetchReturn, ExtraOptions > { - constructor( - log: Logger, - // Needed because it doesn't affect on anything here but being explicit creates a lot of pain down the line - // eslint-disable-next-line @typescript-eslint/no-explicit-any - collectorOptions: UsageCollectorOptions - ) { + constructor(log: Logger, collectorOptions: UsageCollectorOptions) { super(log, collectorOptions); } } diff --git a/src/plugins/usage_collection/server/index.ts b/src/plugins/usage_collection/server/index.ts index 74fa77be9843c..907a61a752052 100644 --- a/src/plugins/usage_collection/server/index.ts +++ b/src/plugins/usage_collection/server/index.ts @@ -17,7 +17,6 @@ export type { UsageCollectorOptions, CollectorFetchContext, CollectorFetchMethod, - CollectorOptionsFetchExtendedContext, } from './collector'; export type { diff --git a/src/plugins/usage_collection/server/mocks.ts b/src/plugins/usage_collection/server/mocks.ts index 6f7d4f19cbaf1..ac7ad69ed4bce 100644 --- a/src/plugins/usage_collection/server/mocks.ts +++ b/src/plugins/usage_collection/server/mocks.ts @@ -9,7 +9,6 @@ import { elasticsearchServiceMock, executionContextServiceMock, - httpServerMock, loggingSystemMock, savedObjectsClientMock, } from '../../../../src/core/server/mocks'; @@ -45,25 +44,14 @@ export const createUsageCollectionSetupMock = () => { return usageCollectionSetupMock; }; -export function createCollectorFetchContextMock(): jest.Mocked> { - const collectorFetchClientsMock: jest.Mocked> = { +export function createCollectorFetchContextMock(): jest.Mocked { + const collectorFetchClientsMock: jest.Mocked = { esClient: elasticsearchServiceMock.createClusterClient().asInternalUser, soClient: savedObjectsClientMock.create(), }; return collectorFetchClientsMock; } -export function createCollectorFetchContextWithKibanaMock(): jest.Mocked< - CollectorFetchContext -> { - const collectorFetchClientsMock: jest.Mocked> = { - esClient: elasticsearchServiceMock.createClusterClient().asInternalUser, - soClient: savedObjectsClientMock.create(), - kibanaRequest: httpServerMock.createKibanaRequest(), - }; - return collectorFetchClientsMock; -} - export const usageCollectionPluginMock = { createSetupContract: createUsageCollectionSetupMock, }; diff --git a/src/plugins/usage_collection/server/plugin.ts b/src/plugins/usage_collection/server/plugin.ts index f415dd768dc22..7cde8bad706dd 100644 --- a/src/plugins/usage_collection/server/plugin.ts +++ b/src/plugins/usage_collection/server/plugin.ts @@ -15,7 +15,6 @@ import type { Plugin, ElasticsearchClient, SavedObjectsClientContract, - KibanaRequest, } from 'src/core/server'; import type { ConfigType } from './config'; import { CollectorSet } from './collector'; @@ -39,12 +38,8 @@ export interface UsageCollectionSetup { * Creates a usage collector to collect plugin telemetry data. * registerCollector must be called to connect the created collector with the service. */ - makeUsageCollector: < - TFetchReturn, - WithKibanaRequest extends boolean = false, - ExtraOptions extends object = {} - >( - options: UsageCollectorOptions + makeUsageCollector: ( + options: UsageCollectorOptions ) => Collector; /** * Register a usage collector or a stats collector. @@ -66,7 +61,6 @@ export interface UsageCollectionSetup { bulkFetch: ( esClient: ElasticsearchClient, soClient: SavedObjectsClientContract, - kibanaRequest: KibanaRequest | undefined, // intentionally `| undefined` to enforce providing the parameter collectors?: Map> ) => Promise>; /** @@ -88,12 +82,8 @@ export interface UsageCollectionSetup { * registerCollector must be called to connect the created collector with the service. * @internal: telemetry and monitoring use */ - makeStatsCollector: < - TFetchReturn, - WithKibanaRequest extends boolean, - ExtraOptions extends object = {} - >( - options: CollectorOptions + makeStatsCollector: ( + options: CollectorOptions ) => Collector; } diff --git a/src/plugins/usage_collection/server/routes/stats/stats.ts b/src/plugins/usage_collection/server/routes/stats/stats.ts index 8e5382d163172..72cbd2e5899ff 100644 --- a/src/plugins/usage_collection/server/routes/stats/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats/stats.ts @@ -15,7 +15,6 @@ import { first } from 'rxjs/operators'; import { ElasticsearchClient, IRouter, - KibanaRequest, MetricsServiceSetup, SavedObjectsClientContract, ServiceStatus, @@ -55,10 +54,9 @@ export function registerStatsRoute({ }) { const getUsage = async ( esClient: ElasticsearchClient, - savedObjectsClient: SavedObjectsClientContract, - kibanaRequest: KibanaRequest + savedObjectsClient: SavedObjectsClientContract ): Promise => { - const usage = await collectorSet.bulkFetchUsage(esClient, savedObjectsClient, kibanaRequest); + const usage = await collectorSet.bulkFetchUsage(esClient, savedObjectsClient); return collectorSet.toObject(usage); }; @@ -97,7 +95,7 @@ export function registerStatsRoute({ const [usage, clusterUuid] = await Promise.all([ shouldGetUsage - ? getUsage(asCurrentUser, savedObjectsClient, req) + ? getUsage(asCurrentUser, savedObjectsClient) : Promise.resolve({}), getClusterUuid(asCurrentUser), ]); diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index 57d21d8719ede..2bc25cfb3c346 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -30,6 +30,7 @@ { "path": "../home/tsconfig.json" }, { "path": "../share/tsconfig.json" }, { "path": "../presentation_util/tsconfig.json" }, + { "path": "../screenshot_mode/tsconfig.json" }, { "path": "../../../x-pack/plugins/spaces/tsconfig.json" } ] } diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_settings_collector.ts b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_settings_collector.ts index 7096647854c15..76cc9adeb43ec 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_settings_collector.ts +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_settings_collector.ts @@ -90,7 +90,6 @@ export function getSettingsCollector( ) { return usageCollection.makeStatsCollector< EmailSettingData | undefined, - false, KibanaSettingsCollectorExtraOptions >({ type: 'kibana_settings', diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_usage_collector.ts b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_usage_collector.ts index cbbfe64f5e3e2..0c952949c56b4 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_usage_collector.ts +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_usage_collector.ts @@ -18,7 +18,7 @@ export function getMonitoringUsageCollector( config: MonitoringConfig, getClient: () => IClusterClient ) { - return usageCollection.makeUsageCollector({ + return usageCollection.makeUsageCollector({ type: 'monitoring', isReady: () => true, schema: { @@ -95,13 +95,8 @@ export function getMonitoringUsageCollector( }, }, }, - extendFetchContext: { - kibanaRequest: true, - }, - fetch: async ({ kibanaRequest }) => { - const callCluster = kibanaRequest - ? getClient().asScoped(kibanaRequest).asCurrentUser - : getClient().asInternalUser; + fetch: async () => { + const callCluster = getClient().asInternalUser; const usageClusters: MonitoringClusterStackProductUsage[] = []; const availableCcs = config.ui.ccs.enabled; const clusters = await fetchClusters(callCluster); diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/register_monitoring_telemetry_collection.ts b/x-pack/plugins/monitoring/server/telemetry_collection/register_monitoring_telemetry_collection.ts index bce6f57d6f950..344b04fb4780d 100644 --- a/x-pack/plugins/monitoring/server/telemetry_collection/register_monitoring_telemetry_collection.ts +++ b/x-pack/plugins/monitoring/server/telemetry_collection/register_monitoring_telemetry_collection.ts @@ -34,13 +34,9 @@ export function registerMonitoringTelemetryCollection( getClient: () => IClusterClient, maxBucketSize: number ) { - const monitoringStatsCollector = usageCollection.makeStatsCollector< - MonitoringTelemetryUsage, - true - >({ + const monitoringStatsCollector = usageCollection.makeStatsCollector({ type: 'monitoringTelemetry', isReady: () => true, - extendFetchContext: { kibanaRequest: true }, schema: { stats: { type: 'array', @@ -137,13 +133,13 @@ export function registerMonitoringTelemetryCollection( }, }, }, - fetch: async ({ kibanaRequest, esClient }) => { + fetch: async () => { const timestamp = Date.now(); // Collect the telemetry from the monitoring indices for this moment. // NOTE: Usually, the monitoring indices index stats for each product every 10s (by default). // However, some data may be delayed up-to 24h because monitoring only collects extended Kibana stats in that interval // to avoid overloading of the system when retrieving data from the collectors (that delay is dealt with in the Kibana Stats getter inside the `getAllStats` method). // By 8.x, we expect to stop collecting the Kibana extended stats and keep only the monitoring-related metrics. - const callCluster = kibanaRequest ? esClient : getClient().asInternalUser; + const callCluster = getClient().asInternalUser; const clusterDetails = await getClusterUuids(callCluster, timestamp, maxBucketSize); const [licenses, stats] = await Promise.all([ getLicenses(clusterDetails, callCluster, maxBucketSize), diff --git a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts index 9b66792efcd9e..b7a52a7a41bcf 100644 --- a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts +++ b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts @@ -9,7 +9,7 @@ import { merge } from 'lodash'; import { loggingSystemMock } from 'src/core/server/mocks'; import { Collector, - createCollectorFetchContextWithKibanaMock, + createCollectorFetchContextMock, createUsageCollectionSetupMock, } from 'src/plugins/usage_collection/server/mocks'; import { HealthStatus } from '../monitoring'; @@ -26,7 +26,7 @@ describe('registerTaskManagerUsageCollector', () => { it('should report telemetry on the ephemeral queue', async () => { const monitoringStats$ = new Subject(); const usageCollectionMock = createUsageCollectionSetupMock(); - const fetchContext = createCollectorFetchContextWithKibanaMock(); + const fetchContext = createCollectorFetchContextMock(); usageCollectionMock.makeUsageCollector.mockImplementation((config) => { collector = new Collector(logger, config); return createUsageCollectionSetupMock().makeUsageCollector(config); @@ -53,7 +53,7 @@ describe('registerTaskManagerUsageCollector', () => { it('should report telemetry on the excluded task types', async () => { const monitoringStats$ = new Subject(); const usageCollectionMock = createUsageCollectionSetupMock(); - const fetchContext = createCollectorFetchContextWithKibanaMock(); + const fetchContext = createCollectorFetchContextMock(); usageCollectionMock.makeUsageCollector.mockImplementation((config) => { collector = new Collector(logger, config); return createUsageCollectionSetupMock().makeUsageCollector(config); diff --git a/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts b/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts index 7febebc2a5179..e1bea8d1aa0e1 100644 --- a/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts +++ b/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts @@ -112,7 +112,6 @@ describe('Telemetry Collection: Get Aggregated Stats', () => { esClient, usageCollection, soClient, - kibanaRequest: undefined, refreshCache: false, }, context @@ -135,7 +134,6 @@ describe('Telemetry Collection: Get Aggregated Stats', () => { esClient, usageCollection, soClient, - kibanaRequest: undefined, refreshCache: false, }, context @@ -163,7 +161,6 @@ describe('Telemetry Collection: Get Aggregated Stats', () => { esClient, usageCollection, soClient, - kibanaRequest: undefined, refreshCache: false, }, context diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c35b5dbe66678..3df1c0a71b095 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3394,12 +3394,6 @@ "home.addData.uploadFileButtonLabel": "ファイルをアップロード", "home.breadcrumbs.homeTitle": "ホーム", "home.breadcrumbs.integrationsAppTitle": "統合", - "home.dataManagementDisableCollection": " 収集を停止するには、", - "home.dataManagementDisableCollectionLink": "ここで使用状況データを無効にします。", - "home.dataManagementDisclaimerPrivacy": "使用状況データがどのように製品とサービスの管理と改善につながるのかに関する詳細については ", - "home.dataManagementDisclaimerPrivacyLink": "プライバシーポリシーをご覧ください。", - "home.dataManagementEnableCollection": " 収集を開始するには、", - "home.dataManagementEnableCollectionLink": "ここで使用状況データを有効にします。", "home.exploreButtonLabel": "独りで閲覧", "home.exploreYourDataDescription": "すべてのステップを終えたら、データ閲覧準備の完了です。", "home.header.title": "ようこそホーム", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f706762740ad8..0326413eb0ac8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3402,12 +3402,6 @@ "home.addData.uploadFileButtonLabel": "上传文件", "home.breadcrumbs.homeTitle": "主页", "home.breadcrumbs.integrationsAppTitle": "集成", - "home.dataManagementDisableCollection": " 要停止收集,", - "home.dataManagementDisableCollectionLink": "请在此禁用使用情况数据。", - "home.dataManagementDisclaimerPrivacy": "要了解使用情况数据如何帮助我们管理和改善产品和服务,请参阅我们的 ", - "home.dataManagementDisclaimerPrivacyLink": "隐私声明。", - "home.dataManagementEnableCollection": " 要启动收集,", - "home.dataManagementEnableCollectionLink": "请在此处启用使用情况数据。", "home.exploreButtonLabel": "自己浏览", "home.exploreYourDataDescription": "完成所有步骤后,您便可以随时浏览自己的数据。", "home.header.title": "欢迎归来", diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry.ts b/x-pack/test/api_integration/apis/telemetry/telemetry.ts index 088678a74813b..4b0137ab5f842 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry.ts @@ -10,6 +10,7 @@ import moment from 'moment'; import type SuperTest from 'supertest'; import deepmerge from 'deepmerge'; import type { FtrProviderContext } from '../../ftr_provider_context'; +import type { SecurityService } from '../../../../../test/common/services/security/security'; import multiClusterFixture from './fixtures/multicluster.json'; import basicClusterFixture from './fixtures/basiccluster.json'; @@ -90,10 +91,31 @@ function updateMonitoringDates( ]); } +async function createUserWithRole( + security: SecurityService, + userName: string, + roleName: string, + role: unknown +) { + await security.role.create(roleName, role); + + await security.user.create(userName, { + password: password(userName), + roles: [roleName], + full_name: `User ${userName}`, + }); +} + +function password(userName: string) { + return `${userName}-password`; +} + export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); // We need this because `.auth` in the already authed one does not work as expected const esArchiver = getService('esArchiver'); const esSupertest = getService('esSupertest'); + const security = getService('security'); describe('/api/telemetry/v2/clusters/_stats', () => { const timestamp = new Date().toISOString(); @@ -236,5 +258,114 @@ export default function ({ getService }: FtrProviderContext) { expect(new Date(fetchedAt).getTime()).to.be.greaterThan(now); }); }); + + describe('Only global read+ users can fetch unencrypted telemetry', () => { + describe('superadmin user', () => { + it('should return unencrypted telemetry for the admin user', async () => { + await supertest + .post('/api/telemetry/v2/clusters/_stats') + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: true }) + .expect(200); + }); + + it('should return encrypted telemetry for the admin user', async () => { + await supertest + .post('/api/telemetry/v2/clusters/_stats') + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: false }) + .expect(200); + }); + }); + + describe('global-read user', () => { + const globalReadOnlyUser = 'telemetry-global-read-only-user'; + const globalReadOnlyRole = 'telemetry-global-read-only-role'; + + before('create user', async () => { + await createUserWithRole(security, globalReadOnlyUser, globalReadOnlyRole, { + kibana: [ + { + spaces: ['*'], + base: ['read'], + feature: {}, + }, + ], + }); + }); + + after(async () => { + await security.user.delete(globalReadOnlyUser); + await security.role.delete(globalReadOnlyRole); + }); + + it('should return encrypted telemetry for the global-read user', async () => { + await supertestWithoutAuth + .post('/api/telemetry/v2/clusters/_stats') + .auth(globalReadOnlyUser, password(globalReadOnlyUser)) + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: false }) + .expect(200); + }); + + it('should return unencrypted telemetry for the global-read user', async () => { + await supertestWithoutAuth + .post('/api/telemetry/v2/clusters/_stats') + .auth(globalReadOnlyUser, password(globalReadOnlyUser)) + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: true }) + .expect(200); + }); + }); + + describe('non global-read user', () => { + const noGlobalUser = 'telemetry-no-global-user'; + const noGlobalRole = 'telemetry-no-global-role'; + + before('create user', async () => { + await createUserWithRole(security, noGlobalUser, noGlobalRole, { + kibana: [ + { + spaces: ['*'], + base: [], + feature: { + // It has access to many features specified individually but not a global one + discover: ['all'], + dashboard: ['all'], + canvas: ['all'], + maps: ['all'], + ml: ['all'], + visualize: ['all'], + dev_tools: ['all'], + }, + }, + ], + }); + }); + + after(async () => { + await security.user.delete(noGlobalUser); + await security.role.delete(noGlobalRole); + }); + + it('should return encrypted telemetry for the read-only user', async () => { + await supertestWithoutAuth + .post('/api/telemetry/v2/clusters/_stats') + .auth(noGlobalUser, password(noGlobalUser)) + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: false }) + .expect(200); + }); + + it('should return 403 when the read-only user requests unencrypted telemetry', async () => { + await supertestWithoutAuth + .post('/api/telemetry/v2/clusters/_stats') + .auth(noGlobalUser, password(noGlobalUser)) + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: true }) + .expect(403); + }); + }); + }); }); } From c0a653de4079227fa02c2beb1fe9536efec1d222 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 7 Mar 2022 15:35:09 -0500 Subject: [PATCH 038/140] [Alerting] Adding functional tests for alerting and actions telemetry (#126528) * Renaming getTestAlertData to getTestRuleData * Adding run now route to alerting_api_integration tests * Renaming getTestAlertData to getTestRuleData * Adding alerting telemetry functional test * Adding actions telemetry functional test * Changing order of tests * Clearing telemetry task state before running tests * Debugging * Cleanup * Moving to own file * Merge Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../tests/alerts/basic_noop_alert_type.ts | 10 +- .../tests/alerts/gold_noop_alert_type.ts | 8 +- .../fixtures/plugins/alerts/server/plugin.ts | 22 +- .../fixtures/plugins/alerts/server/routes.ts | 42 ++- .../common/lib/alert_utils.ts | 4 +- ...st_alert_data.ts => get_test_rule_data.ts} | 2 +- .../common/lib/index.ts | 2 +- .../tests/actions/get_all.ts | 4 +- .../tests/alerting/alerts.ts | 8 +- .../tests/alerting/create.ts | 24 +- .../tests/alerting/delete.ts | 14 +- .../tests/alerting/disable.ts | 14 +- .../tests/alerting/enable.ts | 14 +- .../tests/alerting/event_log.ts | 4 +- .../tests/alerting/excluded.ts | 4 +- .../tests/alerting/execution_status.ts | 4 +- .../tests/alerting/find.ts | 18 +- .../security_and_spaces/tests/alerting/get.ts | 12 +- .../tests/alerting/get_alert_state.ts | 8 +- .../tests/alerting/get_alert_summary.ts | 8 +- .../tests/alerting/health.ts | 4 +- .../tests/alerting/mustache_templates.ts | 4 +- .../tests/alerting/mute_all.ts | 10 +- .../tests/alerting/mute_instance.ts | 12 +- .../tests/alerting/unmute_all.ts | 10 +- .../tests/alerting/unmute_instance.ts | 10 +- .../tests/alerting/update.ts | 26 +- .../tests/alerting/update_api_key.ts | 14 +- .../security_and_spaces/tests/index.ts | 1 + .../tests/telemetry/actions_telemetry.ts | 241 ++++++++++++++ .../tests/telemetry/alerting_telemetry.ts | 304 ++++++++++++++++++ .../tests/telemetry/index.ts | 26 ++ .../preconfigured_alert_history_connector.ts | 4 +- .../spaces_only/tests/alerting/aggregate.ts | 4 +- .../spaces_only/tests/alerting/alerts_base.ts | 12 +- .../spaces_only/tests/alerting/create.ts | 26 +- .../spaces_only/tests/alerting/delete.ts | 8 +- .../spaces_only/tests/alerting/disable.ts | 2 +- .../spaces_only/tests/alerting/enable.ts | 8 +- .../spaces_only/tests/alerting/ephemeral.ts | 4 +- .../spaces_only/tests/alerting/event_log.ts | 10 +- .../tests/alerting/event_log_alerts.ts | 4 +- .../tests/alerting/execution_status.ts | 16 +- .../spaces_only/tests/alerting/find.ts | 10 +- .../spaces_only/tests/alerting/get.ts | 8 +- .../tests/alerting/get_alert_state.ts | 4 +- .../tests/alerting/get_alert_summary.ts | 18 +- .../spaces_only/tests/alerting/monitoring.ts | 10 +- .../tests/alerting/mustache_templates.ts | 12 +- .../spaces_only/tests/alerting/mute_all.ts | 6 +- .../tests/alerting/mute_instance.ts | 6 +- .../spaces_only/tests/alerting/notify_when.ts | 8 +- .../tests/alerting/scheduled_task_id.ts | 6 +- .../spaces_only/tests/alerting/unmute_all.ts | 6 +- .../tests/alerting/unmute_instance.ts | 6 +- .../spaces_only/tests/alerting/update.ts | 8 +- .../tests/alerting/update_api_key.ts | 8 +- 57 files changed, 865 insertions(+), 237 deletions(-) rename x-pack/test/alerting_api_integration/common/lib/{get_test_alert_data.ts => get_test_rule_data.ts} (91%) create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/actions_telemetry.ts create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/alerting_telemetry.ts create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/index.ts diff --git a/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts b/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts index 805f4440909ec..a176d4e73ccf3 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts @@ -5,19 +5,19 @@ * 2.0. */ -import { getTestAlertData } from '../../../common/lib'; +import { getTestRuleData } from '../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export -export default function basicAlertTest({ getService }: FtrProviderContext) { +export default function basicRuleTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe('basic alert', () => { - it('should return 200 when creating a basic license alert', async () => { + describe('basic rule', () => { + it('should return 200 when creating a basic license rule', async () => { await supertest .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); }); }); diff --git a/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts b/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts index 9e66282d42454..5726ad8d5d86c 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts @@ -5,19 +5,19 @@ * 2.0. */ -import { getTestAlertData } from '../../../common/lib'; +import { getTestRuleData } from '../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function emailTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe('create gold noop alert', () => { - it('should return 403 when creating an gold alert', async () => { + describe('create gold noop rule', () => { + it('should return 403 when creating an gold rule', async () => { await supertest .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ rule_type_id: 'test.gold.noop' })) + .send(getTestRuleData({ rule_type_id: 'test.gold.noop' })) .expect(403, { statusCode: 403, error: 'Forbidden', diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts index 7937a9a2db92c..0f345c81f08b4 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts @@ -5,9 +5,15 @@ * 2.0. */ -import { Plugin, CoreSetup, Logger, PluginInitializerContext } from 'kibana/server'; +import { Plugin, CoreSetup, CoreStart, Logger, PluginInitializerContext } from 'kibana/server'; +import { Subject } from 'rxjs'; +import { first } from 'rxjs/operators'; import { PluginSetupContract as ActionsPluginSetup } from '../../../../../../../plugins/actions/server/plugin'; import { PluginSetupContract as AlertingPluginSetup } from '../../../../../../../plugins/alerting/server/plugin'; +import { + TaskManagerSetupContract, + TaskManagerStartContract, +} from '../../../../../../../plugins/task_manager/server/plugin'; import { EncryptedSavedObjectsPluginStart } from '../../../../../../../plugins/encrypted_saved_objects/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server'; import { defineAlertTypes } from './alert_types'; @@ -21,6 +27,7 @@ export interface FixtureSetupDeps { features: FeaturesPluginSetup; actions: ActionsPluginSetup; alerting: AlertingPluginSetup; + taskManager: TaskManagerSetupContract; } export interface FixtureStartDeps { @@ -28,11 +35,17 @@ export interface FixtureStartDeps { security?: SecurityPluginStart; spaces?: SpacesPluginStart; actions: ActionsPluginStart; + taskManager: TaskManagerStartContract; } export class FixturePlugin implements Plugin { private readonly logger: Logger; + taskManagerStart$: Subject = new Subject(); + taskManagerStart: Promise = this.taskManagerStart$ + .pipe(first()) + .toPromise(); + constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get('fixtures', 'plugins', 'alerts'); } @@ -127,9 +140,12 @@ export class FixturePlugin implements Plugin, { logger }: { logger: Logger }) { +export function defineRoutes( + core: CoreSetup, + taskManagerStart: Promise, + { logger }: { logger: Logger } +) { const router = core.http.createRouter(); router.put( { @@ -324,4 +329,39 @@ export function defineRoutes(core: CoreSetup, { logger }: { lo } } ); + + router.post( + { + path: `/api/alerting_actions_telemetry/run_now`, + validate: { + body: schema.object({ + taskId: schema.string({ + validate: (telemetryTaskId: string) => { + if ( + ['Alerting-alerting_telemetry', 'Actions-actions_telemetry'].includes( + telemetryTaskId + ) + ) { + return; + } + return 'invalid telemetry task id'; + }, + }), + }), + }, + }, + async function ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + const { taskId } = req.body; + try { + const taskManager = await taskManagerStart; + return res.ok({ body: await taskManager.runNow(taskId) }); + } catch (err) { + return res.ok({ body: { id: taskId, error: `${err}` } }); + } + } + ); } diff --git a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts index ea16351b49543..9da73e1ca6f43 100644 --- a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts +++ b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts @@ -9,7 +9,7 @@ import { Space, User } from '../types'; import { ObjectRemover } from './object_remover'; import { getUrlPrefix } from './space_test_utils'; import { ES_TEST_INDEX_NAME } from './es_test_index_tool'; -import { getTestAlertData } from './get_test_alert_data'; +import { getTestRuleData } from './get_test_rule_data'; export interface AlertUtilsOpts { user?: User; @@ -293,7 +293,7 @@ export class AlertUtils { request = request.auth(this.user.username, this.user.password); } const response = await request.send({ - ...getTestAlertData(), + ...getTestRuleData(), ...overwrites, }); if (response.statusCode === 200) { diff --git a/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts b/x-pack/test/alerting_api_integration/common/lib/get_test_rule_data.ts similarity index 91% rename from x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts rename to x-pack/test/alerting_api_integration/common/lib/get_test_rule_data.ts index 22dc93b110a07..ace220a5e81de 100644 --- a/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts +++ b/x-pack/test/alerting_api_integration/common/lib/get_test_rule_data.ts @@ -5,7 +5,7 @@ * 2.0. */ -export function getTestAlertData(overwrites = {}) { +export function getTestRuleData(overwrites = {}) { return { enabled: true, name: 'abc', diff --git a/x-pack/test/alerting_api_integration/common/lib/index.ts b/x-pack/test/alerting_api_integration/common/lib/index.ts index 305c42b5c1d64..df7895ed03f6a 100644 --- a/x-pack/test/alerting_api_integration/common/lib/index.ts +++ b/x-pack/test/alerting_api_integration/common/lib/index.ts @@ -8,7 +8,7 @@ export { ObjectRemover } from './object_remover'; export { getUrlPrefix } from './space_test_utils'; export { ES_TEST_INDEX_NAME, ESTestIndexTool } from './es_test_index_tool'; -export { getTestAlertData } from './get_test_alert_data'; +export { getTestRuleData } from './get_test_rule_data'; export { AlertUtils, getConsumerUnauthorizedErrorMessage, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts index 5692e5dd8f8b2..8ae50b9158487 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -134,7 +134,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ actions: [ { group: 'default', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index c3e3c4fc93005..37455149a2a42 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -14,7 +14,7 @@ import { ESTestIndexTool, ES_TEST_INDEX_NAME, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, AlertUtils, getConsumerUnauthorizedErrorMessage, @@ -494,7 +494,7 @@ instanceStateValue: true .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.always-firing', params: { index: ES_TEST_INDEX_NAME, @@ -603,7 +603,7 @@ instanceStateValue: true .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.authorization', params: { callClusterAuthorizationIndex: authorizationIndex, @@ -711,7 +711,7 @@ instanceStateValue: true .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.always-firing', params: { index: ES_TEST_INDEX_NAME, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts index eaa73facb3734..3044142e3c54c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { UserAtSpaceScenarios } from '../../scenarios'; import { checkAAD, - getTestAlertData, + getTestRuleData, getConsumerUnauthorizedErrorMessage, getUrlPrefix, ObjectRemover, @@ -57,7 +57,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ actions: [ { id: createdAction.id, @@ -155,7 +155,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) @@ -194,7 +194,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) @@ -244,7 +244,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.noop', consumer: 'alerts', }) @@ -290,7 +290,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.noop', consumer: 'some consumer patrick invented', }) @@ -325,7 +325,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) - .send(getTestAlertData({ enabled: false })); + .send(getTestRuleData({ enabled: false })); switch (scenario.id) { case 'no_kibana_privileges at space1': @@ -361,7 +361,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ name: ' leading and trailing whitespace ', }) ); @@ -400,7 +400,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unregistered-alert-type', }) ); @@ -458,7 +458,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.validation', }) ); @@ -500,7 +500,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) - .send(getTestAlertData(getTestAlertData({ schedule: { interval: '10x' } }))); + .send(getTestRuleData({ schedule: { interval: '10x' } })); switch (scenario.id) { case 'no_kibana_privileges at space1': @@ -527,7 +527,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) - .send(getTestAlertData(getTestAlertData({ schedule: { interval: '0s' } }))); + .send(getTestRuleData({ schedule: { interval: '0s' } })); switch (scenario.id) { case 'no_kibana_privileges at space1': diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts index d43fb2e7d835f..93e39a011ba3a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { UserAtSpaceScenarios } from '../../scenarios'; import { getUrlPrefix, - getTestAlertData, + getTestRuleData, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, ObjectRemover, @@ -42,7 +42,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); const response = await supertestWithoutAuth @@ -91,7 +91,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) @@ -144,7 +144,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) @@ -211,7 +211,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.noop', consumer: 'alerts', }) @@ -270,7 +270,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -303,7 +303,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); await retry.try(async () => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts index 66f01000ede5e..8a4266eb8dc8a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, @@ -58,7 +58,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: true, actions: [ { @@ -121,7 +121,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', enabled: true, @@ -170,7 +170,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', enabled: true, @@ -230,7 +230,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.noop', consumer: 'alerts', enabled: true, @@ -285,7 +285,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: true })) + .send(getTestRuleData({ enabled: true })) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -351,7 +351,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertest .post(`${getUrlPrefix('other')}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: true })) + .send(getTestRuleData({ enabled: true })) .expect(200); objectRemover.add('other', createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts index 1589a63cb7108..205bfe3fda2ab 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, @@ -60,7 +60,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, actions: [ { @@ -146,7 +146,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', enabled: false, @@ -195,7 +195,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', enabled: false, @@ -249,7 +249,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.noop', consumer: 'alerts', enabled: false, @@ -304,7 +304,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -375,7 +375,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex const { body: createdAlert } = await supertest .post(`${getUrlPrefix('other')}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add('other', createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts index 940203a9b1f8c..04ff3d929dc15 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover, getEventLog } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover, getEventLog } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { validateEvent } from '../../../spaces_only/tests/alerting/event_log'; @@ -27,7 +27,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(spaceId)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.noop', schedule: { interval: '1s' }, throttle: null, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/excluded.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/excluded.ts index add30b178c7e6..eae80da85dc59 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/excluded.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/excluded.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { UserAtSpaceScenarios } from '../../scenarios'; import { - getTestAlertData, + getTestRuleData, getUrlPrefix, ObjectRemover, getEventLog, @@ -57,7 +57,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(scenario.user.username, scenario.user.password) .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.always-firing', schedule: { interval: '1s' }, throttle: '1s', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts index 2bae1c541bc48..dba73cba184dd 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { AlertExecutionStatusErrorReasons } from '../../../../../plugins/alerting/common'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -28,7 +28,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(spaceId)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.noop', schedule: { interval: '1s' }, }) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts index 3274e25e48301..f6ba70e7c2197 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts @@ -10,7 +10,7 @@ import { SuperTest, Test } from 'supertest'; import { chunk, omit } from 'lodash'; import uuid from 'uuid'; import { UserAtSpaceScenarios } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; const findTestUtils = ( @@ -29,7 +29,7 @@ const findTestUtils = ( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -94,7 +94,7 @@ const findTestUtils = ( it('should filter out types that the user is not authorized to `get` retaining pagination', async () => { async function createNoOpAlert(overrides = {}) { - const alert = getTestAlertData(overrides); + const alert = getTestRuleData(overrides); const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') @@ -212,7 +212,7 @@ const findTestUtils = ( .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, actions: [ { @@ -297,7 +297,7 @@ const findTestUtils = ( .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, tags: [myTag], rule_type_id: 'test.restricted-noop', @@ -312,7 +312,7 @@ const findTestUtils = ( .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ tags: [myTag], rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', @@ -374,7 +374,7 @@ const findTestUtils = ( .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, tags: [myTag], rule_type_id: 'test.restricted-noop', @@ -389,7 +389,7 @@ const findTestUtils = ( .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ tags: [myTag], rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', @@ -451,7 +451,7 @@ const findTestUtils = ( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts index 05b053f468a69..6d072b2e26f45 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts @@ -10,7 +10,7 @@ import { SuperTest, Test } from 'supertest'; import { UserAtSpaceScenarios } from '../../scenarios'; import { getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, @@ -32,7 +32,7 @@ const getTestUtils = ( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -96,7 +96,7 @@ const getTestUtils = ( .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) @@ -143,7 +143,7 @@ const getTestUtils = ( .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) @@ -201,7 +201,7 @@ const getTestUtils = ( .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) @@ -258,7 +258,7 @@ const getTestUtils = ( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts index e00d8e53e438e..3bdfe49464fcf 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { getUrlPrefix, ObjectRemover, - getTestAlertData, + getTestRuleData, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, } from '../../../common/lib'; @@ -33,7 +33,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -69,7 +69,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) @@ -123,7 +123,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_summary.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_summary.ts index 3becd487116f7..eb4e592a91d8a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_summary.ts @@ -10,7 +10,7 @@ import { omit } from 'lodash'; import { getUrlPrefix, ObjectRemover, - getTestAlertData, + getTestRuleData, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, } from '../../../common/lib'; @@ -34,7 +34,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo const { body: createdRule } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); @@ -98,7 +98,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) @@ -154,7 +154,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo const { body: createdRule } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdRule.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/health.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/health.ts index 22bf2cdc4204b..d51cf8cc96af9 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/health.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/health.ts @@ -10,7 +10,7 @@ import { UserAtSpaceScenarios } from '../../scenarios'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, AlertUtils, ESTestIndexTool, @@ -105,7 +105,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ schedule: { interval: '5m', }, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts index 8344d4a281ba1..7e3a7599a73e0 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts @@ -19,7 +19,7 @@ import httpProxy from 'http-proxy'; import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { getSlackServer } from '../../../common/fixtures/plugins/actions_simulators/server/plugin'; import { getHttpProxyServer } from '../../../common/lib/get_proxy_server'; @@ -81,7 +81,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces[0].id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ name: 'testing context variable kibanaBaseUrl', rule_type_id: 'test.patternFiring', params: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts index 993b66353756f..bb570e5754e99 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, @@ -49,7 +49,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, actions: [ { @@ -117,7 +117,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', @@ -173,7 +173,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', @@ -240,7 +240,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.restricted-noop', consumer: 'alerts', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts index 4948737e0778a..3948f910423a9 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, @@ -49,7 +49,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, actions: [ { @@ -117,7 +117,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', @@ -173,7 +173,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', @@ -240,7 +240,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.restricted-noop', consumer: 'alerts', @@ -306,7 +306,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts index 526f809033646..f9c1bce2b0318 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, @@ -49,7 +49,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, actions: [ { @@ -122,7 +122,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', @@ -183,7 +183,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', @@ -255,7 +255,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.restricted-noop', consumer: 'alerts', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts index 9c045db888391..17ee25e822a6d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, @@ -49,7 +49,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, actions: [ { @@ -122,7 +122,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', @@ -183,7 +183,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', @@ -255,7 +255,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ enabled: false, rule_type_id: 'test.restricted-noop', consumer: 'alerts', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index e628f0b3d950e..b2a1ae223f62c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -11,7 +11,7 @@ import { UserAtSpaceScenarios } from '../../scenarios'; import { checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, ensureDatetimeIsWithinRange, getConsumerUnauthorizedErrorMessage, @@ -55,7 +55,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -156,7 +156,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) @@ -240,7 +240,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) @@ -335,7 +335,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) @@ -429,7 +429,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -522,7 +522,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -574,7 +574,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -618,7 +618,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -691,7 +691,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.validation', params: { param1: 'test', @@ -753,7 +753,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ + getTestRuleData({ schedule: { interval: '10x' }, enabled: undefined, consumer: undefined, @@ -785,7 +785,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ schedule: { interval: '30m' }, }) ) @@ -931,7 +931,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ schedule: { interval: '1m' }, }) ) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts index a434109a18933..1c25ec550c41e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, getProducerUnauthorizedErrorMessage, @@ -50,7 +50,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ actions: [ { id: createdAction.id, @@ -116,7 +116,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) @@ -170,7 +170,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) @@ -235,7 +235,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) @@ -299,7 +299,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); @@ -363,7 +363,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertest .post(`${getUrlPrefix('other')}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add('other', createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts index 211fe9ec26863..2b26410afeaed 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts @@ -60,6 +60,7 @@ export default function alertingApiIntegrationTests({ loadTestFile }: FtrProvide describe('alerting api integration security and spaces enabled', function () { this.tags('ciGroup17'); + loadTestFile(require.resolve('./telemetry')); loadTestFile(require.resolve('./actions')); loadTestFile(require.resolve('./alerting')); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/actions_telemetry.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/actions_telemetry.ts new file mode 100644 index 0000000000000..350f0019641b8 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/actions_telemetry.ts @@ -0,0 +1,241 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { Spaces, Superuser } from '../../scenarios'; +import { + getUrlPrefix, + getEventLog, + getTestRuleData, + ObjectRemover, + TaskManagerDoc, +} from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createActionsTelemetryTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const retry = getService('retry'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('actions telemetry', () => { + const alwaysFiringRuleId: { [key: string]: string } = {}; + const objectRemover = new ObjectRemover(supertest); + + before(async () => { + // reset the state in the telemetry task + await es.update({ + id: `task:Actions-actions_telemetry`, + index: '.kibana_task_manager', + body: { + doc: { + task: { + state: '{}', + }, + }, + }, + }); + }); + after(() => objectRemover.removeAll()); + + async function createConnector(opts: { name: string; space: string; connectorTypeId: string }) { + const { name, space, connectorTypeId } = opts; + const { body: createdConnector } = await supertestWithoutAuth + .post(`${getUrlPrefix(space)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .auth(Superuser.username, Superuser.password) + .send({ + name, + connector_type_id: connectorTypeId, + config: {}, + secrets: {}, + }) + .expect(200); + objectRemover.add(space, createdConnector.id, 'action', 'actions'); + return createdConnector.id; + } + + async function createRule(opts: { space: string; ruleOverwrites: any }) { + const { ruleOverwrites, space } = opts; + const ruleResponse = await supertestWithoutAuth + .post(`${getUrlPrefix(space)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(Superuser.username, Superuser.password) + .send(getTestRuleData(ruleOverwrites)); + expect(ruleResponse.status).to.eql(200); + objectRemover.add(space, ruleResponse.body.id, 'rule', 'alerting'); + return ruleResponse.body.id; + } + + async function setup() { + // Create rules and connectors in multiple spaces + for (const space of Spaces) { + const noopConnectorId = await createConnector({ + name: 'noop connector', + space: space.id, + connectorTypeId: 'test.noop', + }); + const failingConnectorId = await createConnector({ + name: 'connector that throws', + space: space.id, + connectorTypeId: 'test.throw', + }); + + await createConnector({ + name: 'unused connector', + space: space.id, + connectorTypeId: 'test.excluded', + }); + await createRule({ + space: space.id, + ruleOverwrites: { + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + throttle: null, + notify_when: 'onActiveAlert', + params: {}, + actions: [ + { + id: noopConnectorId, + group: 'default', + params: {}, + }, + ], + }, + }); + await createRule({ + space: space.id, + ruleOverwrites: { + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + throttle: null, + params: {}, + notify_when: 'onActiveAlert', + actions: [ + { + id: failingConnectorId, + group: 'default', + params: {}, + }, + ], + }, + }); + + alwaysFiringRuleId[space.id] = await createRule({ + space: space.id, + ruleOverwrites: { + rule_type_id: 'test.cumulative-firing', + schedule: { interval: '3s' }, + throttle: null, + notify_when: 'onActiveAlert', + params: {}, + actions: [ + { + id: noopConnectorId, + group: 'default', + params: {}, + }, + { + id: failingConnectorId, + group: 'default', + params: {}, + }, + { + id: 'my-slack1', + group: 'other', + params: {}, + }, + ], + }, + }); + } + } + + it('should retrieve telemetry data in the expected format', async () => { + await setup(); + + // let it run for a bit + await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces[0].id, + type: 'alert', + id: alwaysFiringRuleId[Spaces[0].id], + provider: 'alerting', + actions: new Map([['execute', { gte: 5 }]]), + }); + }); + + // request telemetry task to run + await supertest + .post('/api/alerting_actions_telemetry/run_now') + .set('kbn-xsrf', 'xxx') + .send({ taskId: 'Actions-actions_telemetry' }) + .expect(200); + + // get telemetry task doc + const telemetryTask = await es.get({ + id: `task:Actions-actions_telemetry`, + index: '.kibana_task_manager', + }); + const taskState = telemetryTask?._source?.task?.state; + expect(taskState).not.to.be(undefined); + const telemetry = JSON.parse(taskState!); + + // total number of connectors + expect(telemetry.count_total).to.equal(17); + + // total number of active connectors (used by a rule) + expect(telemetry.count_active_total).to.equal(7); + + // total number of connectors broken down by connector type + expect(telemetry.count_by_type['test.throw']).to.equal(3); + expect(telemetry.count_by_type['test.excluded']).to.equal(3); + expect(telemetry.count_by_type['test.noop']).to.equal(3); + expect(telemetry.count_by_type.__slack).to.equal(1); + expect(telemetry.count_by_type['system-abc-action-type']).to.equal(1); + expect(telemetry.count_by_type.__index).to.equal(1); + expect(telemetry.count_by_type['test.index-record']).to.equal(1); + expect(telemetry.count_by_type.__webhook).to.equal(4); + + // total number of active connectors broken down by connector type + expect(telemetry.count_active_by_type['test.throw']).to.equal(3); + expect(telemetry.count_active_by_type['test.noop']).to.equal(3); + expect(telemetry.count_active_by_type.__slack).to.equal(1); + + // total number of rules using the alert history connector + expect(telemetry.count_active_alert_history_connectors).to.equal(0); + + // total number of email connectors used by rules broken down by service type + // testing for existence of this field but we don't have any rules using email + // connectors in this test + expect(telemetry.count_active_email_connectors_by_service_type).to.be.empty(); + + // number of spaces with connectors + expect(telemetry.count_actions_namespaces).to.equal(3); + + // number of action executions - just checking for non-zero as we can't set an exact number + expect(telemetry.count_actions_executions_per_day > 0).to.be(true); + + // number of action executions broken down by connector type + expect(telemetry.count_actions_executions_by_type_per_day['test.noop'] > 0).to.be(true); + + // average execution time - just checking for non-zero as we can't set an exact number + expect(telemetry.avg_execution_time_per_day > 0).to.be(true); + + // average execution time broken down by rule type + expect(telemetry.avg_execution_time_by_type_per_day['test.noop'] > 0).to.be(true); + + // number of failed executions + expect(telemetry.count_actions_executions_failed_per_day > 0).to.be(true); + expect(telemetry.count_actions_executions_failed_by_type_per_day['test.throw'] > 0).to.be( + true + ); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/alerting_telemetry.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/alerting_telemetry.ts new file mode 100644 index 0000000000000..9b8a96bc056ce --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/alerting_telemetry.ts @@ -0,0 +1,304 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { Spaces, Superuser } from '../../scenarios'; +import { + getUrlPrefix, + getEventLog, + getTestRuleData, + ObjectRemover, + TaskManagerDoc, +} from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createAlertingTelemetryTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const retry = getService('retry'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('alerting telemetry', () => { + const alwaysFiringRuleId: { [key: string]: string } = {}; + const objectRemover = new ObjectRemover(supertest); + + before(async () => { + // reset the state in the telemetry task + await es.update({ + id: `task:Alerting-alerting_telemetry`, + index: '.kibana_task_manager', + body: { + doc: { + task: { + state: '{}', + }, + }, + }, + }); + }); + after(() => objectRemover.removeAll()); + + async function createConnector(opts: { name: string; space: string; connectorTypeId: string }) { + const { name, space, connectorTypeId } = opts; + const { body: createdConnector } = await supertestWithoutAuth + .post(`${getUrlPrefix(space)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .auth(Superuser.username, Superuser.password) + .send({ + name, + connector_type_id: connectorTypeId, + config: {}, + secrets: {}, + }) + .expect(200); + objectRemover.add(space, createdConnector.id, 'action', 'actions'); + return createdConnector.id; + } + + async function createRule(opts: { space: string; ruleOverwrites: any }) { + const { ruleOverwrites, space } = opts; + const ruleResponse = await supertestWithoutAuth + .post(`${getUrlPrefix(space)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .auth(Superuser.username, Superuser.password) + .send(getTestRuleData(ruleOverwrites)); + expect(ruleResponse.status).to.eql(200); + objectRemover.add(space, ruleResponse.body.id, 'rule', 'alerting'); + return ruleResponse.body.id; + } + + async function setup() { + // Create rules and connectors in multiple spaces + for (const space of Spaces) { + const noopConnectorId = await createConnector({ + name: 'noop connector', + space: space.id, + connectorTypeId: 'test.noop', + }); + await createConnector({ + name: 'connector that errors', + space: space.id, + connectorTypeId: 'test.throw', + }); + await createRule({ + space: space.id, + ruleOverwrites: { + rule_type_id: 'test.noop', + schedule: { interval: '30s' }, + throttle: '1s', + params: {}, + actions: [ + { + id: noopConnectorId, + group: 'default', + params: {}, + }, + ], + }, + }); + await createRule({ + space: space.id, + ruleOverwrites: { + rule_type_id: 'test.onlyContextVariables', + schedule: { interval: '10s' }, + throttle: '10m', + params: {}, + actions: [ + { + id: noopConnectorId, + group: 'default', + params: {}, + }, + ], + }, + }); + await createRule({ + space: space.id, + ruleOverwrites: { + rule_type_id: 'test.throw', + schedule: { interval: '1m' }, + throttle: '30s', + params: {}, + actions: [ + { + id: noopConnectorId, + group: 'default', + params: {}, + }, + ], + }, + }); + + alwaysFiringRuleId[space.id] = await createRule({ + space: space.id, + ruleOverwrites: { + rule_type_id: 'example.always-firing', + schedule: { interval: '3s' }, + throttle: null, + params: {}, + actions: [ + { + id: noopConnectorId, + group: 'small', + params: {}, + }, + { + id: noopConnectorId, + group: 'medium', + params: {}, + }, + { + id: noopConnectorId, + group: 'large', + params: {}, + }, + ], + }, + }); + + await createRule({ + space: space.id, + ruleOverwrites: { + rule_type_id: 'test.noop', + schedule: { interval: '5m' }, + throttle: null, + enabled: false, + params: {}, + actions: [ + { + id: noopConnectorId, + group: 'default', + params: {}, + }, + ], + }, + }); + } + } + + it('should retrieve telemetry data in the expected format', async () => { + await setup(); + + // let it run for a bit + await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: Spaces[0].id, + type: 'alert', + id: alwaysFiringRuleId[Spaces[0].id], + provider: 'alerting', + actions: new Map([['execute', { gte: 5 }]]), + }); + }); + + // request telemetry task to run + await supertest + .post('/api/alerting_actions_telemetry/run_now') + .set('kbn-xsrf', 'xxx') + .send({ taskId: 'Alerting-alerting_telemetry' }) + .expect(200); + + // get telemetry task doc + const telemetryTask = await es.get({ + id: `task:Alerting-alerting_telemetry`, + index: '.kibana_task_manager', + }); + const taskState = telemetryTask?._source?.task?.state; + expect(taskState).not.to.be(undefined); + const telemetry = JSON.parse(taskState!); + + // total number of rules + expect(telemetry.count_total).to.equal(15); + + // total number of enabled rules + expect(telemetry.count_active_total).to.equal(12); + + // total number of disabled rules + expect(telemetry.count_disabled_total).to.equal(3); + + // total number of rules broken down by rule type + expect(telemetry.count_by_type.test__onlyContextVariables).to.equal(3); + expect(telemetry.count_by_type['example__always-firing']).to.equal(3); + expect(telemetry.count_by_type.test__throw).to.equal(3); + expect(telemetry.count_by_type.test__noop).to.equal(6); + + // total number of enabled rules broken down by rule type + expect(telemetry.count_active_by_type.test__onlyContextVariables).to.equal(3); + expect(telemetry.count_active_by_type['example__always-firing']).to.equal(3); + expect(telemetry.count_active_by_type.test__throw).to.equal(3); + expect(telemetry.count_active_by_type.test__noop).to.equal(3); + + // throttle time stats + expect(telemetry.throttle_time.min).to.equal('0s'); + expect(telemetry.throttle_time.avg).to.equal('157.75s'); + expect(telemetry.throttle_time.max).to.equal('600s'); + expect(telemetry.throttle_time_number_s.min).to.equal(0); + expect(telemetry.throttle_time_number_s.avg).to.equal(157.75); + expect(telemetry.throttle_time_number_s.max).to.equal(600); + + // schedule interval stats + expect(telemetry.schedule_time.min).to.equal('3s'); + expect(telemetry.schedule_time.avg).to.equal('80.6s'); + expect(telemetry.schedule_time.max).to.equal('300s'); + expect(telemetry.schedule_time_number_s.min).to.equal(3); + expect(telemetry.schedule_time_number_s.avg).to.equal(80.6); + expect(telemetry.schedule_time_number_s.max).to.equal(300); + + // attached connectors stats + expect(telemetry.connectors_per_alert.min).to.equal(1); + expect(telemetry.connectors_per_alert.avg).to.equal(1.4); + expect(telemetry.connectors_per_alert.max).to.equal(3); + + // number of spaces with rules + expect(telemetry.count_rules_namespaces).to.equal(3); + + // number of rule executions - just checking for non-zero as we can't set an exact number + // each rule should have had a chance to execute once + expect(telemetry.count_rules_executions_per_day >= 15).to.be(true); + + // number of rule executions broken down by rule type + expect(telemetry.count_by_type.test__onlyContextVariables >= 3).to.be(true); + expect(telemetry.count_by_type['example__always-firing'] >= 3).to.be(true); + expect(telemetry.count_by_type.test__throw >= 3).to.be(true); + expect(telemetry.count_by_type.test__noop >= 3).to.be(true); + + // average execution time - just checking for non-zero as we can't set an exact number + expect(telemetry.avg_execution_time_per_day > 0).to.be(true); + + // average execution time broken down by rule type + expect(telemetry.avg_execution_time_by_type_per_day.test__onlyContextVariables > 0).to.be( + true + ); + expect(telemetry.avg_execution_time_by_type_per_day['example__always-firing'] > 0).to.be( + true + ); + expect(telemetry.avg_execution_time_by_type_per_day.test__throw > 0).to.be(true); + expect(telemetry.avg_execution_time_by_type_per_day.test__noop > 0).to.be(true); + + // number of failed executions - we have one rule that always fails + expect(telemetry.count_rules_executions_failured_per_day >= 1).to.be(true); + expect(telemetry.count_rules_executions_failured_by_reason_per_day.execute >= 1).to.be(true); + expect( + telemetry.count_rules_executions_failured_by_reason_by_type_per_day.execute.test__throw >= 1 + ).to.be(true); + + // number of execution timeouts - testing for existence of this field but + // this test doesn't have any rules that timeout + expect(telemetry.count_rules_executions_timeouts_per_day).to.equal(0); + expect(telemetry.count_rules_executions_timeouts_by_type_per_day).to.be.empty(); + + // number of failed/unrecognized tasks - testing for existence of this field but + // this test doesn't have any unrecognized rule types + expect(telemetry.count_failed_and_unrecognized_rule_tasks_per_day).to.equal(0); + expect(telemetry.count_failed_and_unrecognized_rule_tasks_by_status_per_day).to.be.empty(); + expect( + telemetry.count_failed_and_unrecognized_rule_tasks_by_status_by_type_per_day + ).to.be.empty(); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/index.ts new file mode 100644 index 0000000000000..9e73fafc9f7bd --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/telemetry/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { setupSpacesAndUsers, tearDown } from '..'; + +// eslint-disable-next-line import/no-default-export +export default function actionsTests({ loadTestFile, getService }: FtrProviderContext) { + describe('Alerting and Actions Telemetry', () => { + before(async () => { + await setupSpacesAndUsers(getService); + }); + + after(async () => { + await tearDown(getService); + }); + + // run telemetry tests before anything else + loadTestFile(require.resolve('./actions_telemetry')); + loadTestFile(require.resolve('./alerting_telemetry')); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/preconfigured_alert_history_connector.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/preconfigured_alert_history_connector.ts index dea873073f61f..274e147898d9b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/preconfigured_alert_history_connector.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/preconfigured_alert_history_connector.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { getTestAlertData, ObjectRemover } from '../../../../common/lib'; +import { getTestRuleData, ObjectRemover } from '../../../../common/lib'; import { AlertHistoryDefaultIndexName } from '../../../../../../plugins/actions/common'; const ALERT_HISTORY_OVERRIDE_INDEX = 'kibana-alert-history-not-the-default'; @@ -27,7 +27,7 @@ export default function preconfiguredAlertHistoryConnectorTests({ const alertId = 'instance'; function getTestData(params = {}) { - return getTestAlertData({ + return getTestRuleData({ rule_type_id: ruleTypeId, schedule: { interval: '1s' }, params: { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts index cf7ebffef85a2..961a15dd5223d 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -239,7 +239,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData(testAlertOverrides)) + .send(getTestRuleData(testAlertOverrides)) .expect(200); await waitForStatus(createdAlert.id, new Set([status])); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts index ea818a6e64b0d..58ddd5516d8a5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts @@ -16,7 +16,7 @@ import { ESTestIndexTool, ES_TEST_INDEX_NAME, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, AlertUtils, ensureDatetimeIsWithinRange, @@ -198,7 +198,7 @@ instanceStateValue: true .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, throttle: null, @@ -259,7 +259,7 @@ instanceStateValue: true .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, enabled: false, @@ -354,7 +354,7 @@ instanceStateValue: true .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ schedule: { interval: '1m' }, rule_type_id: 'test.always-firing', params: { @@ -427,7 +427,7 @@ instanceStateValue: true .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.authorization', params: { callClusterAuthorizationIndex: authorizationIndex, @@ -473,7 +473,7 @@ instanceStateValue: true .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.always-firing', params: { index: ES_TEST_INDEX_NAME, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts index b002e0668dc52..1d5eb16ff3f89 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts @@ -11,7 +11,7 @@ import { Spaces } from '../../scenarios'; import { checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getConsumerUnauthorizedErrorMessage, TaskManagerDoc, @@ -53,7 +53,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ actions: [ { id: createdAction.id, @@ -132,7 +132,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ actions: [ { id: createdAction.id, @@ -244,7 +244,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ params: { ignoredButPersisted: lotsOfSpaces, }, @@ -288,7 +288,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ params: { risk_score: 40, severity: 'medium', @@ -325,7 +325,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { const response = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()); + .send(getTestRuleData()); expect(response.status).to.eql(200); objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); @@ -344,7 +344,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { const response = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()); + .send(getTestRuleData()); expect(response.status).to.eql(200); objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); @@ -363,7 +363,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { const response = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()); + .send(getTestRuleData()); expect(response.status).to.eql(400); expect(response.body).to.eql({ @@ -379,13 +379,13 @@ export default function createAlertTests({ getService }: FtrProviderContext) { const createdAlertResponse = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlertResponse.body.id, 'rule', 'alerting'); await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(409); }); @@ -393,7 +393,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { const response = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ consumer: 'some consumer patrick invented' })); + .send(getTestRuleData({ consumer: 'some consumer patrick invented' })); expect(response.status).to.eql(403); expect(response.body).to.eql({ @@ -411,7 +411,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { const response = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })); + .send(getTestRuleData({ enabled: false })); expect(response.status).to.eql(200); objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); @@ -435,7 +435,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { rule_type_id: alertTypeId, notify_when: notifyWhen, ...testAlert - } = getTestAlertData({ + } = getTestRuleData({ actions: [ { id: createdAction.id, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts index 0a2df70b6316a..073d76dc859a5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -31,7 +31,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); await supertest @@ -51,7 +51,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); await supertest @@ -69,7 +69,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); await supertest diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts index 51cb54aa5f9e5..2a1d27a4d3b39 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts @@ -12,7 +12,7 @@ import { AlertUtils as RuleUtils, checkAAD, getUrlPrefix, - getTestAlertData as getTestRuleData, + getTestRuleData, ObjectRemover, getEventLog, } from '../../../common/lib'; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts index 611c2498dd9d3..c0c56ed354a84 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, TaskManagerDoc, } from '../../../common/lib'; @@ -40,7 +40,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -72,7 +72,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.other.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.other.id, createdAlert.id, 'rule', 'alerting'); @@ -88,7 +88,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/ephemeral.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/ephemeral.ts index a3b8c75f79e62..ac095fd4c4419 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/ephemeral.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/ephemeral.ts @@ -11,7 +11,7 @@ import { Spaces } from '../../scenarios'; import { getUrlPrefix, ObjectRemover, - getTestAlertData, + getTestRuleData, getEventLog, ESTestIndexTool, ES_TEST_INDEX_NAME, @@ -69,7 +69,7 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext const pattern = { instance: [true, true, true, false, true, true], }; - const alertData = getTestAlertData({ + const alertData = getTestRuleData({ rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1m' }, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts index c36f9a0da75bc..2cc2044653fd9 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts @@ -10,7 +10,7 @@ import uuid from 'uuid'; import { Spaces } from '../../scenarios'; import { getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, getEventLog, ESTestIndexTool, @@ -62,7 +62,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, throttle: null, @@ -300,7 +300,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.multipleSearches', schedule: { interval: '1s' }, throttle: null, @@ -405,7 +405,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, throttle: null, @@ -599,7 +599,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.throw', schedule: { interval: '1s' }, throttle: null, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log_alerts.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log_alerts.ts index 6fa3eb1a43b62..6d7f95df10d9b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log_alerts.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log_alerts.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover, getEventLog } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover, getEventLog } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { IValidatedEvent } from '../../../../../plugins/event_log/server'; @@ -31,7 +31,7 @@ export default function eventLogAlertTests({ getService }: FtrProviderContext) { .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, throttle: null, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts index 0f7ed80cfd38d..d5bcd0c7a9ae2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts @@ -10,7 +10,7 @@ import { Spaces } from '../../scenarios'; import { checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, ensureDatetimesAreOrdered, } from '../../../common/lib'; @@ -30,7 +30,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon const response = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()); + .send(getTestRuleData()); const dateEnd = Date.now(); expect(response.status).to.eql(200); objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); @@ -61,7 +61,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.noop', schedule: { interval: '1s' }, }) @@ -94,7 +94,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, params: { @@ -130,7 +130,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.throw', schedule: { interval: '1s' }, }) @@ -166,7 +166,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.throw', schedule: { interval: '1s' }, }) @@ -188,7 +188,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.validation', schedule: { interval: '1s' }, params: { param1: 'valid now, but will change to a number soon!' }, @@ -227,7 +227,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.throw', schedule: { interval: '1s' }, }) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts index 7a4a91bd575bb..5ab632c6a66b8 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { SuperTest, Test } from 'supertest'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; async function createAlert( @@ -19,7 +19,7 @@ async function createAlert( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData(overwrites)) + .send(getTestRuleData(overwrites)) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); return createdAlert; @@ -36,7 +36,7 @@ const findTestUtils = ( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -82,7 +82,7 @@ const findTestUtils = ( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -266,7 +266,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts index 9a4be8951f8f0..81f67c8d49e33 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { SuperTest, Test } from 'supertest'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; const getTestUtils = ( @@ -22,7 +22,7 @@ const getTestUtils = ( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -64,7 +64,7 @@ const getTestUtils = ( const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -113,7 +113,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts index 318dfdfe065df..61d38b522bb59 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, ObjectRemover, getTestAlertData } from '../../../common/lib'; +import { getUrlPrefix, ObjectRemover, getTestRuleData } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -24,7 +24,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_summary.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_summary.ts index 8cd9a0bbb1290..d13da4694bbe2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_summary.ts @@ -12,7 +12,7 @@ import { Spaces } from '../../scenarios'; import { getUrlPrefix, ObjectRemover, - getTestAlertData, + getTestRuleData, AlertUtils, getEventLog, } from '../../../common/lib'; @@ -44,7 +44,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo const { body: createdRule } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); @@ -83,7 +83,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo const { body: createdRule } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); @@ -122,7 +122,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo const { body: createdRule } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); @@ -142,7 +142,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo const { body: createdRule } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); @@ -165,7 +165,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo const { body: createdRule } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); @@ -189,7 +189,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo const { body: createdRule } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ rule_type_id: 'test.throw' })) + .send(getTestRuleData({ rule_type_id: 'test.throw' })) .expect(200); objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); @@ -216,7 +216,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, @@ -271,7 +271,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/monitoring.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/monitoring.ts index 9b91d395d16c6..c08a28b3c3ca3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/monitoring.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/monitoring.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -23,7 +23,7 @@ export default function monitoringAlertTests({ getService }: FtrProviderContext) const createResponse = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ schedule: { interval: '3s' } })); + .send(getTestRuleData({ schedule: { interval: '3s' } })); expect(createResponse.status).to.eql(200); objectRemover.add(Spaces.space1.id, createResponse.body.id, 'rule', 'alerting'); @@ -44,7 +44,7 @@ export default function monitoringAlertTests({ getService }: FtrProviderContext) const createResponse = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ schedule: { interval: '3s' } })); + .send(getTestRuleData({ schedule: { interval: '3s' } })); expect(createResponse.status).to.eql(200); objectRemover.add(Spaces.space1.id, createResponse.body.id, 'rule', 'alerting'); @@ -69,7 +69,7 @@ export default function monitoringAlertTests({ getService }: FtrProviderContext) .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternSuccessOrFailure', schedule: { interval: '3s' }, params: { @@ -102,7 +102,7 @@ export default function monitoringAlertTests({ getService }: FtrProviderContext) .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ schedule: { interval: '3s' }, }) ); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts index 8d300733bafc3..ff596db062b75 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts @@ -19,7 +19,7 @@ import axios from 'axios'; import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { getWebhookServer, @@ -88,7 +88,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ name: 'testing variable escapes for webhook', rule_type_id: 'test.patternFiring', params: { @@ -139,7 +139,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ name: 'testing variable escapes for slack', rule_type_id: 'test.patternFiring', params: { @@ -189,7 +189,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ name: 'testing context variable expansion', rule_type_id: 'test.patternFiring', params: { @@ -239,7 +239,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ name: 'testing context variable kibanaBaseUrl', rule_type_id: 'test.patternFiring', params: { @@ -290,7 +290,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ name: 'testing variable escapes for webhook', rule_type_id: 'test.patternFiring', params: { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts index c21a13edbf2cb..27475049ac9a6 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, } from '../../../common/lib'; @@ -30,7 +30,7 @@ export default function createMuteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -56,7 +56,7 @@ export default function createMuteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts index afe29280748a5..d32b74fd39447 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, } from '../../../common/lib'; @@ -30,7 +30,7 @@ export default function createMuteInstanceTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -56,7 +56,7 @@ export default function createMuteInstanceTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts index 7f1b82614a100..5049f7c863a06 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { getUrlPrefix, ObjectRemover, getTestAlertData, getEventLog } from '../../../common/lib'; +import { getUrlPrefix, ObjectRemover, getTestRuleData, getEventLog } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { IValidatedEvent } from '../../../../../plugins/event_log/server'; @@ -55,7 +55,7 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, @@ -131,7 +131,7 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, @@ -222,7 +222,7 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ + getTestRuleData({ rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/scheduled_task_id.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/scheduled_task_id.ts index 9f087b7392132..a83cd4241d144 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/scheduled_task_id.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/scheduled_task_id.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { getUrlPrefix, TaskManagerDoc, ObjectRemover, getTestAlertData } from '../../../common/lib'; +import { getUrlPrefix, TaskManagerDoc, ObjectRemover, getTestRuleData } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; const MIGRATED_RULE_ID = '74f3e6d7-b7bb-477d-ac28-92ee22728e6e'; @@ -47,7 +47,7 @@ export default function createScheduledTaskIdTests({ getService }: FtrProviderCo await supertest .post(`${getUrlPrefix(``)}/api/alerting/rule/${MIGRATED_TASK_ID}`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(409); }); @@ -94,7 +94,7 @@ export default function createScheduledTaskIdTests({ getService }: FtrProviderCo const response = await supertestWithoutAuth .post(`${getUrlPrefix(``)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()); + .send(getTestRuleData()); expect(response.status).to.eql(200); objectRemover.add('default', response.body.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts index 2fffa9189e0ad..47f61250157a3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, } from '../../../common/lib'; @@ -30,7 +30,7 @@ export default function createUnmuteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -57,7 +57,7 @@ export default function createUnmuteTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts index e0c42136628d3..086f40d9febae 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, } from '../../../common/lib'; @@ -30,7 +30,7 @@ export default function createUnmuteInstanceTests({ getService }: FtrProviderCon const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -57,7 +57,7 @@ export default function createUnmuteInstanceTests({ getService }: FtrProviderCon const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ enabled: false })) + .send(getTestRuleData({ enabled: false })) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts index d97ca18c52d4a..c5a9c93d45e81 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; -import { checkAAD, getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { checkAAD, getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -23,7 +23,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -94,7 +94,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -124,7 +124,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts index 78ceadec44a9a..9fe5c7e112c79 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts @@ -12,7 +12,7 @@ import { AlertUtils, checkAAD, getUrlPrefix, - getTestAlertData, + getTestRuleData, ObjectRemover, } from '../../../common/lib'; @@ -34,7 +34,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); @@ -59,7 +59,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.other.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.other.id, createdAlert.id, 'rule', 'alerting'); @@ -75,7 +75,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte const { body: createdAlert } = await supertestWithoutAuth .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData()) + .send(getTestRuleData()) .expect(200); objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); From 2c7fe22a39ce3037fd699a9bc054eb6f0d754f01 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 7 Mar 2022 16:40:07 -0500 Subject: [PATCH 039/140] Revert "[CI] Expand spot instance trial a bit (#126928)" This reverts commit 79935f3341f4d234b79ea0c5f7f3de817134556b. --- .buildkite/pipelines/hourly.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 8c1162954cfac..e5bc841774fde 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -19,7 +19,7 @@ steps: label: 'Default CI Group' parallelism: 27 agents: - queue: n2-4-spot + queue: n2-4 depends_on: build timeout_in_minutes: 250 key: default-cigroup @@ -31,7 +31,7 @@ steps: - command: CI_GROUP=Docker .buildkite/scripts/steps/functional/xpack_cigroup.sh label: 'Docker CI Group' agents: - queue: n2-4-spot + queue: n2-4 depends_on: build timeout_in_minutes: 120 key: default-cigroup-docker @@ -44,7 +44,7 @@ steps: label: 'OSS CI Group' parallelism: 11 agents: - queue: n2-4-spot + queue: ci-group-4d depends_on: build timeout_in_minutes: 120 key: oss-cigroup @@ -98,7 +98,7 @@ steps: - command: .buildkite/scripts/steps/functional/oss_firefox.sh label: 'OSS Firefox Tests' agents: - queue: n2-4-spot + queue: ci-group-4d depends_on: build timeout_in_minutes: 120 retry: @@ -130,7 +130,7 @@ steps: - command: .buildkite/scripts/steps/functional/oss_misc.sh label: 'OSS Misc Functional Tests' agents: - queue: n2-4-spot + queue: n2-4 depends_on: build timeout_in_minutes: 120 retry: From 9b79234be985f01afc18dc265ebed291bc425b6f Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 7 Mar 2022 16:40:20 -0500 Subject: [PATCH 040/140] Revert "[ci] Configure hourly pipeline for a small spot instance trial (#126824)" This reverts commit 891b6cd83b62b21a67ef8b625ea5c05d2f0c68a3. --- .buildkite/pipelines/hourly.yml | 72 +++++---------------------------- 1 file changed, 9 insertions(+), 63 deletions(-) diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index e5bc841774fde..78c57ff3bd128 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -56,44 +56,24 @@ steps: - command: .buildkite/scripts/steps/functional/oss_accessibility.sh label: 'OSS Accessibility Tests' agents: - queue: n2-4-spot + queue: ci-group-4d depends_on: build timeout_in_minutes: 120 retry: automatic: - - exit_status: '1' + - exit_status: '*' limit: 1 - - exit_status: '-1' - limit: 3 - - exit_status: '130' - limit: 3 - - exit_status: '137' - limit: 3 - - exit_status: '143' - limit: 3 - - exit_status: '255' - limit: 3 - command: .buildkite/scripts/steps/functional/xpack_accessibility.sh label: 'Default Accessibility Tests' agents: - queue: n2-4-spot + queue: n2-4 depends_on: build timeout_in_minutes: 120 retry: automatic: - - exit_status: '1' + - exit_status: '*' limit: 1 - - exit_status: '-1' - limit: 3 - - exit_status: '130' - limit: 3 - - exit_status: '137' - limit: 3 - - exit_status: '143' - limit: 3 - - exit_status: '255' - limit: 3 - command: .buildkite/scripts/steps/functional/oss_firefox.sh label: 'OSS Firefox Tests' @@ -109,23 +89,13 @@ steps: - command: .buildkite/scripts/steps/functional/xpack_firefox.sh label: 'Default Firefox Tests' agents: - queue: n2-4-spot + queue: n2-4 depends_on: build timeout_in_minutes: 120 retry: automatic: - - exit_status: '1' + - exit_status: '*' limit: 1 - - exit_status: '-1' - limit: 3 - - exit_status: '130' - limit: 3 - - exit_status: '137' - limit: 3 - - exit_status: '143' - limit: 3 - - exit_status: '255' - limit: 3 - command: .buildkite/scripts/steps/functional/oss_misc.sh label: 'OSS Misc Functional Tests' @@ -141,23 +111,13 @@ steps: - command: .buildkite/scripts/steps/functional/xpack_saved_object_field_metrics.sh label: 'Saved Object Field Metrics' agents: - queue: n2-4-spot + queue: n2-4 depends_on: build timeout_in_minutes: 120 retry: automatic: - - exit_status: '1' + - exit_status: '*' limit: 1 - - exit_status: '-1' - limit: 3 - - exit_status: '130' - limit: 3 - - exit_status: '137' - limit: 3 - - exit_status: '143' - limit: 3 - - exit_status: '255' - limit: 3 - command: .buildkite/scripts/steps/test/jest.sh label: 'Jest Tests' @@ -178,23 +138,9 @@ steps: - command: .buildkite/scripts/steps/test/api_integration.sh label: 'API Integration Tests' agents: - queue: n2-4-spot + queue: n2-2 timeout_in_minutes: 120 key: api-integration - retry: - automatic: - - exit_status: '1' - limit: 1 - - exit_status: '-1' - limit: 3 - - exit_status: '130' - limit: 3 - - exit_status: '137' - limit: 3 - - exit_status: '143' - limit: 3 - - exit_status: '255' - limit: 3 - command: .buildkite/scripts/steps/lint.sh label: 'Linting' From be7ff2ce8698e1db7ad2d3a9c3b0ce3b8ba3d510 Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 7 Mar 2022 14:04:54 -0800 Subject: [PATCH 041/140] [type-summarizer] always use normalized paths, fix windows compat (#127055) --- packages/kbn-type-summarizer/BUILD.bazel | 6 +- .../src/lib/bazel_cli_config.ts | 85 +++++++++++++------ .../src/lib/is_node_module.ts | 6 +- packages/kbn-type-summarizer/src/lib/path.ts | 36 ++++++++ .../kbn-type-summarizer/src/lib/printer.ts | 3 +- .../src/lib/source_mapper.ts | 12 ++- .../src/lib/tsconfig_file.ts | 2 +- .../{ => src}/tests/integration_helpers.ts | 19 +++-- .../tests/integration_tests/class.test.ts | 2 +- .../tests/integration_tests/function.test.ts | 4 +- .../integration_tests/import_boundary.test.ts | 4 +- .../tests/integration_tests/interface.test.ts | 2 +- .../integration_tests/references.test.ts | 6 +- .../integration_tests/type_alias.test.ts | 2 +- .../tests/integration_tests/variables.test.ts | 2 +- packages/kbn-type-summarizer/tsconfig.json | 3 +- 16 files changed, 130 insertions(+), 64 deletions(-) create mode 100644 packages/kbn-type-summarizer/src/lib/path.ts rename packages/kbn-type-summarizer/{ => src}/tests/integration_helpers.ts (91%) rename packages/kbn-type-summarizer/{ => src}/tests/integration_tests/class.test.ts (98%) rename packages/kbn-type-summarizer/{ => src}/tests/integration_tests/function.test.ts (92%) rename packages/kbn-type-summarizer/{ => src}/tests/integration_tests/import_boundary.test.ts (96%) rename packages/kbn-type-summarizer/{ => src}/tests/integration_tests/interface.test.ts (97%) rename packages/kbn-type-summarizer/{ => src}/tests/integration_tests/references.test.ts (90%) rename packages/kbn-type-summarizer/{ => src}/tests/integration_tests/type_alias.test.ts (96%) rename packages/kbn-type-summarizer/{ => src}/tests/integration_tests/variables.test.ts (97%) diff --git a/packages/kbn-type-summarizer/BUILD.bazel b/packages/kbn-type-summarizer/BUILD.bazel index 13a89e0669b80..ec0df11bc3762 100644 --- a/packages/kbn-type-summarizer/BUILD.bazel +++ b/packages/kbn-type-summarizer/BUILD.bazel @@ -10,10 +10,7 @@ PKG_REQUIRE_NAME = "@kbn/type-summarizer" SOURCE_FILES = glob( [ "src/**/*.ts", - ], - exclude = [ - "**/*.test.*" - ], + ] ) SRCS = SOURCE_FILES @@ -49,6 +46,7 @@ TYPES_DEPS = [ "@npm//is-path-inside", "@npm//normalize-path", "@npm//source-map", + "@npm//strip-ansi", "@npm//tslib", ] diff --git a/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts b/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts index a0fdb3e4685b1..7a0d9be4629cd 100644 --- a/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts +++ b/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import Path from 'path'; import Fs from 'fs'; import { CliError } from './cli_error'; import { parseCliFlags } from './cli_flags'; +import * as Path from './path'; const TYPE_SUMMARIZER_PACKAGES = ['@kbn/type-summarizer', '@kbn/crypto']; @@ -25,6 +25,36 @@ interface BazelCliConfig { use: 'api-extractor' | 'type-summarizer'; } +function isKibanaRepo(dir: string) { + try { + const json = Fs.readFileSync(Path.join(dir, 'package.json'), 'utf8'); + const parsed = JSON.parse(json); + return parsed.name === 'kibana'; + } catch { + return false; + } +} + +function findRepoRoot() { + const start = Path.resolve(__dirname); + let dir = start; + while (true) { + if (isKibanaRepo(dir)) { + return dir; + } + + // this is not the kibana directory, try moving up a directory + const parent = Path.join(dir, '..'); + if (parent === dir) { + throw new Error( + `unable to find Kibana's package.json file when traversing up from [${start}]` + ); + } + + dir = parent; + } +} + export function parseBazelCliFlags(argv: string[]): BazelCliConfig { const { rawFlags, unknownFlags } = parseCliFlags(argv, { string: ['use'], @@ -39,19 +69,7 @@ export function parseBazelCliFlags(argv: string[]): BazelCliConfig { }); } - let REPO_ROOT; - try { - const name = 'utils'; - // eslint-disable-next-line @typescript-eslint/no-var-requires - const utils = require('@kbn/' + name); - REPO_ROOT = utils.REPO_ROOT as string; - } catch (error) { - if (error && error.code === 'MODULE_NOT_FOUND') { - throw new CliError('type-summarizer bazel cli only works after bootstrap'); - } - - throw error; - } + const repoRoot = findRepoRoot(); const [relativePackagePath, ...extraPositional] = rawFlags._; if (typeof relativePackagePath !== 'string') { @@ -70,26 +88,45 @@ export function parseBazelCliFlags(argv: string[]): BazelCliConfig { const packageName: string = JSON.parse( Fs.readFileSync(Path.join(packageDir, 'package.json'), 'utf8') ).name; - const repoRelativePackageDir = Path.relative(REPO_ROOT, packageDir); + const repoRelativePackageDir = Path.relative(repoRoot, packageDir); return { use, packageName, - tsconfigPath: Path.join(REPO_ROOT, repoRelativePackageDir, 'tsconfig.json'), - inputPath: Path.resolve(REPO_ROOT, 'node_modules', packageName, 'target_types/index.d.ts'), + tsconfigPath: Path.join(repoRoot, repoRelativePackageDir, 'tsconfig.json'), + inputPath: Path.join(repoRoot, 'node_modules', packageName, 'target_types/index.d.ts'), repoRelativePackageDir, - outputDir: Path.resolve(REPO_ROOT, 'data/type-summarizer-output', use), + outputDir: Path.join(repoRoot, 'data/type-summarizer-output', use), }; } -export function parseBazelCliJson(json: string): BazelCliConfig { - let config; +function parseJsonFromCli(json: string) { try { - config = JSON.parse(json); + return JSON.parse(json); } catch (error) { - throw new CliError('unable to parse first positional argument as JSON'); + // TODO: This is to handle a bug in Bazel which escapes `"` in .bat arguments incorrectly, replacing them with `\` + if ( + error.message === 'Unexpected token \\ in JSON at position 1' && + process.platform === 'win32' + ) { + const unescapedJson = json.replaceAll('\\', '"'); + try { + return JSON.parse(unescapedJson); + } catch (e) { + throw new CliError( + `unable to parse first positional argument as JSON: "${e.message}"\n unescaped value: ${unescapedJson}\n raw value: ${json}` + ); + } + } + + throw new CliError( + `unable to parse first positional argument as JSON: "${error.message}"\n value: ${json}` + ); } +} +export function parseBazelCliJson(json: string): BazelCliConfig { + const config = parseJsonFromCli(json); if (typeof config !== 'object' || config === null) { throw new CliError('config JSON must be an object'); } @@ -131,14 +168,12 @@ export function parseBazelCliJson(json: string): BazelCliConfig { throw new CliError(`buildFilePath [${buildFilePath}] must be a relative path`); } - const repoRelativePackageDir = Path.dirname(buildFilePath); - return { packageName, outputDir: Path.resolve(outputDir), tsconfigPath: Path.resolve(tsconfigPath), inputPath: Path.resolve(inputPath), - repoRelativePackageDir, + repoRelativePackageDir: Path.dirname(buildFilePath), use: TYPE_SUMMARIZER_PACKAGES.includes(packageName) ? 'type-summarizer' : 'api-extractor', }; } diff --git a/packages/kbn-type-summarizer/src/lib/is_node_module.ts b/packages/kbn-type-summarizer/src/lib/is_node_module.ts index 67efde569a1b4..ba4d607ccb864 100644 --- a/packages/kbn-type-summarizer/src/lib/is_node_module.ts +++ b/packages/kbn-type-summarizer/src/lib/is_node_module.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import Path from 'path'; - import isPathInside from 'is-path-inside'; +import * as Path from './path'; + export function isNodeModule(dtsDir: string, path: string) { return (isPathInside(path, dtsDir) ? Path.relative(dtsDir, path) : path) - .split(Path.sep) + .split('/') .includes('node_modules'); } diff --git a/packages/kbn-type-summarizer/src/lib/path.ts b/packages/kbn-type-summarizer/src/lib/path.ts new file mode 100644 index 0000000000000..79d56aa58fab6 --- /dev/null +++ b/packages/kbn-type-summarizer/src/lib/path.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; + +import normalizePath from 'normalize-path'; +const cwd = normalizePath(process.cwd()); + +export function cwdRelative(path: string) { + return relative(cwd, path); +} + +export function relative(from: string, to: string) { + return normalizePath(Path.relative(from, to)); +} + +export function join(...segments: string[]) { + return Path.join(...segments); +} + +export function dirname(path: string) { + return Path.dirname(path); +} + +export function resolve(path: string) { + return Path.isAbsolute(path) ? normalizePath(path) : join(cwd, path); +} + +export function isAbsolute(path: string) { + return Path.isAbsolute(path); +} diff --git a/packages/kbn-type-summarizer/src/lib/printer.ts b/packages/kbn-type-summarizer/src/lib/printer.ts index 3ce675f727927..8ecc4356ea4a2 100644 --- a/packages/kbn-type-summarizer/src/lib/printer.ts +++ b/packages/kbn-type-summarizer/src/lib/printer.ts @@ -6,11 +6,10 @@ * Side Public License, v 1. */ -import Path from 'path'; - import * as ts from 'typescript'; import { SourceNode, CodeWithSourceMap } from 'source-map'; +import * as Path from './path'; import { findKind } from './ts_nodes'; import { SourceMapper } from './source_mapper'; import { CollectorResult } from './export_collector'; diff --git a/packages/kbn-type-summarizer/src/lib/source_mapper.ts b/packages/kbn-type-summarizer/src/lib/source_mapper.ts index 0b0e69571469c..1f03119e8e3fd 100644 --- a/packages/kbn-type-summarizer/src/lib/source_mapper.ts +++ b/packages/kbn-type-summarizer/src/lib/source_mapper.ts @@ -6,16 +6,14 @@ * Side Public License, v 1. */ -import Path from 'path'; - import * as ts from 'typescript'; import { SourceNode, SourceMapConsumer, BasicSourceMapConsumer } from 'source-map'; -import normalizePath from 'normalize-path'; import { Logger } from './log'; import { tryReadFile } from './helpers/fs'; import { parseJson } from './helpers/json'; import { isNodeModule } from './is_node_module'; +import * as Path from './path'; type SourceMapConsumerEntry = [ts.SourceFile, BasicSourceMapConsumer | undefined]; @@ -38,9 +36,9 @@ export class SourceMapper { return [sourceFile, undefined]; } - const relSourceFile = Path.relative(process.cwd(), sourceFile.fileName); - const sourceMapPath = Path.resolve(Path.dirname(sourceFile.fileName), match[1]); - const relSourceMapPath = Path.relative(process.cwd(), sourceMapPath); + const relSourceFile = Path.cwdRelative(sourceFile.fileName); + const sourceMapPath = Path.join(Path.dirname(sourceFile.fileName), match[1]); + const relSourceMapPath = Path.cwdRelative(sourceMapPath); const sourceJson = await tryReadFile(sourceMapPath, 'utf8'); if (!sourceJson) { throw new Error( @@ -81,7 +79,7 @@ export class SourceMapper { * us the path to the source, relative to the `repoRelativePackageDir`. */ fixSourcePath(source: string) { - return normalizePath(Path.relative(this.sourceFixDir, Path.join('/', source))); + return Path.relative(this.sourceFixDir, Path.join('/', source)); } getSourceNode(generatedNode: ts.Node, code: string) { diff --git a/packages/kbn-type-summarizer/src/lib/tsconfig_file.ts b/packages/kbn-type-summarizer/src/lib/tsconfig_file.ts index 7d327b1f03e0a..f3d491c93abcb 100644 --- a/packages/kbn-type-summarizer/src/lib/tsconfig_file.ts +++ b/packages/kbn-type-summarizer/src/lib/tsconfig_file.ts @@ -7,8 +7,8 @@ */ import * as ts from 'typescript'; -import Path from 'path'; +import * as Path from './path'; import { CliError } from './cli_error'; export function readTsConfigFile(path: string) { diff --git a/packages/kbn-type-summarizer/tests/integration_helpers.ts b/packages/kbn-type-summarizer/src/tests/integration_helpers.ts similarity index 91% rename from packages/kbn-type-summarizer/tests/integration_helpers.ts rename to packages/kbn-type-summarizer/src/tests/integration_helpers.ts index 68e1f3cc3a3b0..c64e58c4e33f9 100644 --- a/packages/kbn-type-summarizer/tests/integration_helpers.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_helpers.ts @@ -13,13 +13,14 @@ import Fsp from 'fs/promises'; import * as ts from 'typescript'; import stripAnsi from 'strip-ansi'; +import normalizePath from 'normalize-path'; -import { loadTsConfigFile } from '../src/lib/tsconfig_file'; -import { createTsProject } from '../src/lib/ts_project'; -import { TestLog } from '../src/lib/log'; -import { summarizePackage } from '../src/summarize_package'; +import { loadTsConfigFile } from '../lib/tsconfig_file'; +import { createTsProject } from '../lib/ts_project'; +import { TestLog } from '../lib/log'; +import { summarizePackage } from '../summarize_package'; -const TMP_DIR = Path.resolve(__dirname, '__tmp__'); +const TMP_DIR = Path.resolve(__dirname, '../../__tmp__'); const DIAGNOSTIC_HOST = { getCanonicalFileName: (p: string) => p, @@ -153,11 +154,11 @@ class MockCli { // summarize the .d.ts files into the output dir await summarizePackage(log, { - dtsDir: this.dtsOutputDir, - inputPaths: [this.inputPath], - outputDir: this.outputDir, + dtsDir: normalizePath(this.dtsOutputDir), + inputPaths: [normalizePath(this.inputPath)], + outputDir: normalizePath(this.outputDir), repoRelativePackageDir: 'src', - tsconfigPath: this.tsconfigPath, + tsconfigPath: normalizePath(this.tsconfigPath), strictPrinting: false, }); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/class.test.ts b/packages/kbn-type-summarizer/src/tests/integration_tests/class.test.ts similarity index 98% rename from packages/kbn-type-summarizer/tests/integration_tests/class.test.ts rename to packages/kbn-type-summarizer/src/tests/integration_tests/class.test.ts index d2b39ab69d47b..eaf87cda8521b 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/class.test.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_tests/class.test.ts @@ -69,7 +69,7 @@ it('prints basic class correctly', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] debug Ignoring 1 global declarations for \\"Record\\" debug Ignoring 5 global declarations for \\"Promise\\" " diff --git a/packages/kbn-type-summarizer/tests/integration_tests/function.test.ts b/packages/kbn-type-summarizer/src/tests/integration_tests/function.test.ts similarity index 92% rename from packages/kbn-type-summarizer/tests/integration_tests/function.test.ts rename to packages/kbn-type-summarizer/src/tests/integration_tests/function.test.ts index ec15d941ca153..de0f1bb4c6d4c 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/function.test.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_tests/function.test.ts @@ -75,8 +75,8 @@ it('prints the function declaration, including comments', async () => { `); expect(result.logs).toMatchInlineSnapshot(` "debug loaded sourcemaps for [ - 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/bar.d.ts', - 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' + 'packages/kbn-type-summarizer/__tmp__/dist_dts/bar.d.ts', + 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] " `); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/import_boundary.test.ts b/packages/kbn-type-summarizer/src/tests/integration_tests/import_boundary.test.ts similarity index 96% rename from packages/kbn-type-summarizer/tests/integration_tests/import_boundary.test.ts rename to packages/kbn-type-summarizer/src/tests/integration_tests/import_boundary.test.ts index 35cf08e297359..f1e3279bb57b0 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/import_boundary.test.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_tests/import_boundary.test.ts @@ -52,7 +52,7 @@ it('output type links to named import from node modules', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] " `); }); @@ -84,7 +84,7 @@ it('output type links to default import from node modules', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] " `); }); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/interface.test.ts b/packages/kbn-type-summarizer/src/tests/integration_tests/interface.test.ts similarity index 97% rename from packages/kbn-type-summarizer/tests/integration_tests/interface.test.ts rename to packages/kbn-type-summarizer/src/tests/integration_tests/interface.test.ts index cc821f1c9fc90..cbccbfb1d77dc 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/interface.test.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_tests/interface.test.ts @@ -55,7 +55,7 @@ it('prints the whole interface, including comments', async () => { } `); expect(result.logs).toMatchInlineSnapshot(` - "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] debug Ignoring 5 global declarations for \\"Promise\\" " `); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/references.test.ts b/packages/kbn-type-summarizer/src/tests/integration_tests/references.test.ts similarity index 90% rename from packages/kbn-type-summarizer/tests/integration_tests/references.test.ts rename to packages/kbn-type-summarizer/src/tests/integration_tests/references.test.ts index 796bcd5fac3d1..0a2cc9aaf5857 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/references.test.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_tests/references.test.ts @@ -60,9 +60,9 @@ it('collects references from source files which contribute to result', async () `); expect(result.logs).toMatchInlineSnapshot(` "debug loaded sourcemaps for [ - 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/files/foo.d.ts', - 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/files/index.d.ts', - 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' + 'packages/kbn-type-summarizer/__tmp__/dist_dts/files/foo.d.ts', + 'packages/kbn-type-summarizer/__tmp__/dist_dts/files/index.d.ts', + 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] debug Ignoring 5 global declarations for \\"Promise\\" debug Ignoring 4 global declarations for \\"Symbol\\" diff --git a/packages/kbn-type-summarizer/tests/integration_tests/type_alias.test.ts b/packages/kbn-type-summarizer/src/tests/integration_tests/type_alias.test.ts similarity index 96% rename from packages/kbn-type-summarizer/tests/integration_tests/type_alias.test.ts rename to packages/kbn-type-summarizer/src/tests/integration_tests/type_alias.test.ts index f099bad9f3de6..cbe99c54ca042 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/type_alias.test.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_tests/type_alias.test.ts @@ -36,7 +36,7 @@ it('prints basic type alias', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] " `); }); diff --git a/packages/kbn-type-summarizer/tests/integration_tests/variables.test.ts b/packages/kbn-type-summarizer/src/tests/integration_tests/variables.test.ts similarity index 97% rename from packages/kbn-type-summarizer/tests/integration_tests/variables.test.ts rename to packages/kbn-type-summarizer/src/tests/integration_tests/variables.test.ts index c51c9b0098b6c..a2b47d6471025 100644 --- a/packages/kbn-type-summarizer/tests/integration_tests/variables.test.ts +++ b/packages/kbn-type-summarizer/src/tests/integration_tests/variables.test.ts @@ -62,7 +62,7 @@ it('prints basic variable exports with sourcemaps', async () => { } `); expect(output.logs).toMatchInlineSnapshot(` - "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/tests/__tmp__/dist_dts/index.d.ts' ] + "debug loaded sourcemaps for [ 'packages/kbn-type-summarizer/__tmp__/dist_dts/index.d.ts' ] " `); }); diff --git a/packages/kbn-type-summarizer/tsconfig.json b/packages/kbn-type-summarizer/tsconfig.json index f3c3802071ac4..b3779bdd686ea 100644 --- a/packages/kbn-type-summarizer/tsconfig.json +++ b/packages/kbn-type-summarizer/tsconfig.json @@ -11,7 +11,6 @@ ] }, "include": [ - "src/**/*", - "tests/**/*" + "src/**/*" ] } From 0821c31fa53287ddae4123fc8d4fb7ead3933013 Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 7 Mar 2022 14:27:41 -0800 Subject: [PATCH 042/140] [ftr] implement support for accessing ES through CCS (#126547) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- packages/kbn-test/BUILD.bazel | 2 + .../kbn-test/src/es/es_client_for_testing.ts | 16 +++ packages/kbn-test/src/es/index.ts | 6 +- packages/kbn-test/src/es/test_es_cluster.ts | 11 +- .../lib/config/schema.ts | 10 +- .../functional_tests/lib/run_elasticsearch.ts | 109 +++++++++++++++--- .../kbn-test/src/functional_tests/tasks.ts | 12 +- packages/kbn-test/src/index.ts | 1 + scripts/functional_tests.js | 2 +- test/common/services/es_archiver.ts | 3 +- .../services/security/system_indices_user.ts | 44 ++++--- test/common/services/security/test_user.ts | 22 ++++ .../kbn_archiver/date_nested_ccs.json | 4 +- .../fixtures/kbn_archiver/discover_ccs.json | 6 +- .../{_data_view_ccs.ts => data_view_ccs.ts} | 16 ++- test/functional_ccs/apps/discover/index.ts | 30 ++--- ...ed_queries_ccs.ts => saved_queries_ccs.ts} | 16 +-- test/functional_ccs/config.js | 25 ---- test/functional_ccs/config.ts | 52 +++++++++ ...r_context.d.ts => ftr_provider_context.ts} | 4 +- test/functional_ccs/services/index.ts | 17 +++ test/functional_ccs/services/remote_es.ts | 24 ++++ .../services/remote_es_archiver.ts | 22 ++++ 23 files changed, 341 insertions(+), 113 deletions(-) rename test/functional_ccs/apps/discover/{_data_view_ccs.ts => data_view_ccs.ts} (85%) rename test/functional_ccs/apps/discover/{_saved_queries_ccs.ts => saved_queries_ccs.ts} (93%) delete mode 100644 test/functional_ccs/config.js create mode 100644 test/functional_ccs/config.ts rename test/functional_ccs/{apps/discover/ftr_provider_context.d.ts => ftr_provider_context.ts} (80%) create mode 100644 test/functional_ccs/services/index.ts create mode 100644 test/functional_ccs/services/remote_es.ts create mode 100644 test/functional_ccs/services/remote_es_archiver.ts diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel index 6732b08d8bc72..4dc8d684941f3 100644 --- a/packages/kbn-test/BUILD.bazel +++ b/packages/kbn-test/BUILD.bazel @@ -53,6 +53,7 @@ RUNTIME_DEPS = [ "@npm//execa", "@npm//exit-hook", "@npm//form-data", + "@npm//get-port", "@npm//getopts", "@npm//globby", "@npm//he", @@ -90,6 +91,7 @@ TYPES_DEPS = [ "@npm//del", "@npm//exit-hook", "@npm//form-data", + "@npm//get-port", "@npm//getopts", "@npm//jest", "@npm//jest-cli", diff --git a/packages/kbn-test/src/es/es_client_for_testing.ts b/packages/kbn-test/src/es/es_client_for_testing.ts index 084cb8d77eac5..3eeccffcc2186 100644 --- a/packages/kbn-test/src/es/es_client_for_testing.ts +++ b/packages/kbn-test/src/es/es_client_for_testing.ts @@ -27,6 +27,22 @@ export interface EsClientForTestingOptions extends Omit +) { + const ccsConfig = config.get('esTestCluster.ccs'); + if (!ccsConfig) { + throw new Error('FTR config is missing esTestCluster.ccs'); + } + + return createEsClientForTesting({ + esUrl: ccsConfig.remoteClusterUrl, + requestTimeout: config.get('timeouts.esRequestTimeout'), + ...overrides, + }); +} + export function createEsClientForFtrConfig( config: Config, overrides?: Omit diff --git a/packages/kbn-test/src/es/index.ts b/packages/kbn-test/src/es/index.ts index 641253acc3647..bdc3388945827 100644 --- a/packages/kbn-test/src/es/index.ts +++ b/packages/kbn-test/src/es/index.ts @@ -9,5 +9,9 @@ export { createTestEsCluster } from './test_es_cluster'; export type { CreateTestEsClusterOptions, EsTestCluster, ICluster } from './test_es_cluster'; export { esTestConfig } from './es_test_config'; -export { createEsClientForTesting, createEsClientForFtrConfig } from './es_client_for_testing'; +export { + createEsClientForTesting, + createEsClientForFtrConfig, + createRemoteEsClientForFtrConfig, +} from './es_client_for_testing'; export type { EsClientForTestingOptions } from './es_client_for_testing'; diff --git a/packages/kbn-test/src/es/test_es_cluster.ts b/packages/kbn-test/src/es/test_es_cluster.ts index 6e4fc2fb14628..27f29ce6995a7 100644 --- a/packages/kbn-test/src/es/test_es_cluster.ts +++ b/packages/kbn-test/src/es/test_es_cluster.ts @@ -136,7 +136,15 @@ export interface CreateTestEsClusterOptions { * } */ port?: number; + /** + * Should this ES cluster use SSL? + */ ssl?: boolean; + /** + * Explicit transport port for a single node to run on, or a string port range to use eg. '9300-9400' + * defaults to the transport port from `packages/kbn-test/src/es/es_test_config.ts` + */ + transportPort?: number | string; } export function createTestEsCluster< @@ -155,13 +163,14 @@ export function createTestEsCluster< esJavaOpts, clusterName: customClusterName = 'es-test-cluster', ssl, + transportPort, } = options; const clusterName = `${CI_PARALLEL_PROCESS_PREFIX}${customClusterName}`; const defaultEsArgs = [ `cluster.name=${clusterName}`, - `transport.port=${esTestConfig.getTransportPort()}`, + `transport.port=${transportPort ?? esTestConfig.getTransportPort()}`, // For multi-node clusters, we make all nodes master-eligible by default. ...(nodes.length > 1 ? ['discovery.type=zen', `cluster.initial_master_nodes=${nodes.map((n) => n.name).join(',')}`] diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 42a77b85ddc6c..cf1afbb810c71 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -192,12 +192,17 @@ export const schema = Joi.object() esTestCluster: Joi.object() .keys({ - license: Joi.string().default('basic'), + license: Joi.valid('basic', 'trial', 'gold').default('basic'), from: Joi.string().default('snapshot'), - serverArgs: Joi.array(), + serverArgs: Joi.array().items(Joi.string()), esJavaOpts: Joi.string(), dataArchive: Joi.string(), ssl: Joi.boolean().default(false), + ccs: Joi.object().keys({ + remoteClusterUrl: Joi.string().uri({ + scheme: /https?/, + }), + }), }) .default(), @@ -290,6 +295,7 @@ export const schema = Joi.object() security: Joi.object() .keys({ roles: Joi.object().default(), + remoteEsRoles: Joi.object(), defaultRoles: Joi.array() .items(Joi.string()) .when('$primary', { diff --git a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts index 68e7a4992fcfc..ba314e8325a65 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts +++ b/packages/kbn-test/src/functional_tests/lib/run_elasticsearch.ts @@ -8,6 +8,7 @@ import { resolve } from 'path'; import type { ToolingLog } from '@kbn/dev-utils'; +import getPort from 'get-port'; import { KIBANA_ROOT } from './paths'; import type { Config } from '../../functional_test_runner/'; import { createTestEsCluster } from '../../es'; @@ -15,32 +16,102 @@ import { createTestEsCluster } from '../../es'; interface RunElasticsearchOptions { log: ToolingLog; esFrom?: string; + config: Config; +} + +interface CcsConfig { + remoteClusterUrl: string; } -export async function runElasticsearch({ + +type EsConfig = ReturnType; + +function getEsConfig({ config, - options, -}: { - config: Config; - options: RunElasticsearchOptions; -}) { - const { log, esFrom } = options; - const ssl = config.get('esTestCluster.ssl'); - const license = config.get('esTestCluster.license'); - const esArgs = config.get('esTestCluster.serverArgs'); - const esJavaOpts = config.get('esTestCluster.esJavaOpts'); + esFrom = config.get('esTestCluster.from'), +}: RunElasticsearchOptions) { + const ssl = !!config.get('esTestCluster.ssl'); + const license: 'basic' | 'trial' | 'gold' = config.get('esTestCluster.license'); + const esArgs: string[] = config.get('esTestCluster.serverArgs') ?? []; + const esJavaOpts: string | undefined = config.get('esTestCluster.esJavaOpts'); const isSecurityEnabled = esArgs.includes('xpack.security.enabled=true'); - const cluster = createTestEsCluster({ - port: config.get('servers.elasticsearch.port'), - password: isSecurityEnabled ? 'changeme' : config.get('servers.elasticsearch.password'), + const port: number | undefined = config.get('servers.elasticsearch.port'); + const ccsConfig: CcsConfig | undefined = config.get('esTestCluster.ccs'); + + const password: string | undefined = isSecurityEnabled + ? 'changeme' + : config.get('servers.elasticsearch.password'); + + const dataArchive: string | undefined = config.get('esTestCluster.dataArchive'); + + return { + ssl, license, - log, - basePath: resolve(KIBANA_ROOT, '.es'), - esFrom: esFrom || config.get('esTestCluster.from'), - dataArchive: config.get('esTestCluster.dataArchive'), esArgs, esJavaOpts, - ssl, + isSecurityEnabled, + esFrom, + port, + password, + dataArchive, + ccsConfig, + }; +} + +export async function runElasticsearch( + options: RunElasticsearchOptions +): Promise<() => Promise> { + const { log } = options; + const config = getEsConfig(options); + + if (!config.ccsConfig) { + const node = await startEsNode(log, 'ftr', config); + return async () => { + await node.cleanup(); + }; + } + + const remotePort = await getPort(); + const remoteNode = await startEsNode(log, 'ftr-remote', { + ...config, + port: parseInt(new URL(config.ccsConfig.remoteClusterUrl).port, 10), + transportPort: remotePort, + }); + + const localNode = await startEsNode(log, 'ftr-local', { + ...config, + esArgs: [...config.esArgs, `cluster.remote.ftr-remote.seeds=localhost:${remotePort}`], + }); + + return async () => { + await localNode.cleanup(); + await remoteNode.cleanup(); + }; +} + +async function startEsNode( + log: ToolingLog, + name: string, + config: EsConfig & { transportPort?: number } +) { + const cluster = createTestEsCluster({ + clusterName: `cluster-${name}`, + esArgs: config.esArgs, + esFrom: config.esFrom, + esJavaOpts: config.esJavaOpts, + license: config.license, + password: config.password, + port: config.port, + ssl: config.ssl, + log, + basePath: resolve(KIBANA_ROOT, '.es'), + nodes: [ + { + name, + dataArchive: config.dataArchive, + }, + ], + transportPort: config.transportPort, }); await cluster.start(); diff --git a/packages/kbn-test/src/functional_tests/tasks.ts b/packages/kbn-test/src/functional_tests/tasks.ts index 5906193ca145c..b1213ceef2905 100644 --- a/packages/kbn-test/src/functional_tests/tasks.ts +++ b/packages/kbn-test/src/functional_tests/tasks.ts @@ -108,10 +108,10 @@ export async function runTests(options: RunTestsParams) { await withProcRunner(log, async (procs) => { const config = await readConfigFile(log, options.esVersion, configPath); - let es; + let shutdownEs; try { if (process.env.TEST_ES_DISABLE_STARTUP !== 'true') { - es = await runElasticsearch({ config, options: { ...options, log } }); + shutdownEs = await runElasticsearch({ ...options, log, config }); } await runKibanaServer({ procs, config, options }); await runFtr({ configPath, options: { ...options, log } }); @@ -125,8 +125,8 @@ export async function runTests(options: RunTestsParams) { await procs.stop('kibana'); } finally { - if (es) { - await es.cleanup(); + if (shutdownEs) { + await shutdownEs(); } } } @@ -166,7 +166,7 @@ export async function startServers({ ...options }: StartServerOptions) { await withProcRunner(log, async (procs) => { const config = await readConfigFile(log, options.esVersion, options.config); - const es = await runElasticsearch({ config, options: opts }); + const shutdownEs = await runElasticsearch({ ...opts, config }); await runKibanaServer({ procs, config, @@ -190,7 +190,7 @@ export async function startServers({ ...options }: StartServerOptions) { log.success(makeSuccessMessage(options)); await procs.waitForAllToStop(); - await es.cleanup(); + await shutdownEs(); }); } diff --git a/packages/kbn-test/src/index.ts b/packages/kbn-test/src/index.ts index 26d40f70edb78..c9f0e67c558f1 100644 --- a/packages/kbn-test/src/index.ts +++ b/packages/kbn-test/src/index.ts @@ -36,6 +36,7 @@ export { createTestEsCluster, createEsClientForTesting, createEsClientForFtrConfig, + createRemoteEsClientForFtrConfig, } from './es'; export { diff --git a/scripts/functional_tests.js b/scripts/functional_tests.js index 972c682ab0d9f..b185bcb0ea5d0 100644 --- a/scripts/functional_tests.js +++ b/scripts/functional_tests.js @@ -9,7 +9,7 @@ require('../src/setup_node_env'); require('@kbn/test').runTestsCli([ require.resolve('../test/functional/config.js'), - require.resolve('../test/functional_ccs/config.js'), + require.resolve('../test/functional_ccs/config.ts'), require.resolve('../test/plugin_functional/config.ts'), require.resolve('../test/ui_capabilities/newsfeed_err/config.ts'), require.resolve('../test/new_visualize_flow/config.ts'), diff --git a/test/common/services/es_archiver.ts b/test/common/services/es_archiver.ts index 2ea4b6ce3a434..3212e80928686 100644 --- a/test/common/services/es_archiver.ts +++ b/test/common/services/es_archiver.ts @@ -8,11 +8,12 @@ import { EsArchiver } from '@kbn/es-archiver'; import { FtrProviderContext } from '../ftr_provider_context'; -import * as KibanaServer from './kibana_server'; +import * as KibanaServer from '../../common/services/kibana_server'; export function EsArchiverProvider({ getService }: FtrProviderContext): EsArchiver { const config = getService('config'); const client = getService('es'); + const log = getService('log'); const kibanaServer = getService('kibanaServer'); const retry = getService('retry'); diff --git a/test/common/services/security/system_indices_user.ts b/test/common/services/security/system_indices_user.ts index 2546fbeafffa7..091621207a671 100644 --- a/test/common/services/security/system_indices_user.ts +++ b/test/common/services/security/system_indices_user.ts @@ -6,25 +6,18 @@ * Side Public License, v 1. */ -import { systemIndicesSuperuser, createEsClientForFtrConfig } from '@kbn/test'; +import { Client } from '@elastic/elasticsearch'; +import { ToolingLog } from '@kbn/dev-utils'; +import { + systemIndicesSuperuser, + createEsClientForFtrConfig, + createRemoteEsClientForFtrConfig, +} from '@kbn/test'; import { FtrProviderContext } from '../../ftr_provider_context'; const SYSTEM_INDICES_SUPERUSER_ROLE = 'system_indices_superuser'; -export async function createSystemIndicesUser(ctx: FtrProviderContext) { - const log = ctx.getService('log'); - const config = ctx.getService('config'); - - const enabled = !config - .get('esTestCluster.serverArgs') - .some((arg: string) => arg === 'xpack.security.enabled=false'); - - if (!enabled) { - return; - } - - const es = createEsClientForFtrConfig(config); - +async function ensureSystemIndicesUser(es: Client, log: ToolingLog) { // There are cases where the test config file doesn't have security disabled // but tests are still executed on ES without security. Checking this case // by trying to fetch the users list. @@ -67,3 +60,24 @@ export async function createSystemIndicesUser(ctx: FtrProviderContext) { await es.close(); } + +export async function createSystemIndicesUser(ctx: FtrProviderContext) { + const log = ctx.getService('log'); + const config = ctx.getService('config'); + + const enabled = !config + .get('esTestCluster.serverArgs') + .some((arg: string) => arg === 'xpack.security.enabled=false'); + + if (!enabled) { + return; + } + + const localEs = createEsClientForFtrConfig(config); + await ensureSystemIndicesUser(localEs, log); + + if (config.get('esTestCluster.ccs')) { + const remoteEs = createRemoteEsClientForFtrConfig(config); + await ensureSystemIndicesUser(remoteEs, log); + } +} diff --git a/test/common/services/security/test_user.ts b/test/common/services/security/test_user.ts index 7c4751220fa1f..bc5dbf68698bc 100644 --- a/test/common/services/security/test_user.ts +++ b/test/common/services/security/test_user.ts @@ -90,6 +90,28 @@ export async function createTestUserService(ctx: FtrProviderContext, role: Role, await role.create(name, definition); } + // when configured to setup remote roles, load the remote es service and set them up directly via es + const remoteEsRoles: undefined | Record = config.get('security.remoteEsRoles'); + if (remoteEsRoles) { + let remoteEs; + try { + remoteEs = ctx.getService('remoteEs' as 'es'); + } catch (error) { + throw new Error( + 'unable to load `remoteEs` cluster, which should provide an ES client configured to talk to the remote cluster. Include that service from another FTR config or fix the error it is throwing on creation: ' + + error.message + ); + } + + for (const [name, body] of Object.entries(remoteEsRoles)) { + log.info(`creating ${name} role on remote cluster`); + await remoteEs.security.putRole({ + name, + ...body, + }); + } + } + // delete the test_user if present (will it error if the user doesn't exist?) try { await user.delete(TEST_USER_NAME); diff --git a/test/functional/fixtures/kbn_archiver/date_nested_ccs.json b/test/functional/fixtures/kbn_archiver/date_nested_ccs.json index 933b21d920c00..9a411ba96705a 100644 --- a/test/functional/fixtures/kbn_archiver/date_nested_ccs.json +++ b/test/functional/fixtures/kbn_archiver/date_nested_ccs.json @@ -2,10 +2,10 @@ "attributes": { "fields": "[]", "timeFieldName": "nested.timestamp", - "title": "remote:date-nested" + "title": "ftr-remote:date-nested" }, "coreMigrationVersion": "8.2.0", - "id": "remote:date-nested", + "id": "ftr-remote:date-nested", "migrationVersion": { "index-pattern": "8.0.0" }, diff --git a/test/functional/fixtures/kbn_archiver/discover_ccs.json b/test/functional/fixtures/kbn_archiver/discover_ccs.json index d53aa1bc759af..4c1143ed4e798 100644 --- a/test/functional/fixtures/kbn_archiver/discover_ccs.json +++ b/test/functional/fixtures/kbn_archiver/discover_ccs.json @@ -3,10 +3,10 @@ "fieldAttrs": "{\"referer\":{\"customLabel\":\"Referer custom\"}}", "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"@message\"}}},{\"name\":\"@tags\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"@tags\"}}},{\"name\":\"@timestamp\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"agent\"}}},{\"name\":\"bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extension\"}}},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"headings\"}}},{\"name\":\"host\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"host\"}}},{\"name\":\"id\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"index\"}}},{\"name\":\"ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"links\"}}},{\"name\":\"machine.os\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"machine.os\"}}},{\"name\":\"machine.ram\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"esTypes\":[\"double\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nestedField.child\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"nestedField\"}}},{\"name\":\"phpmemory\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.article:section\"}}},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.article:tag\"}}},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:description\"}}},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:image\"}}},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:image:height\"}}},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:image:width\"}}},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:site_name\"}}},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:title\"}}},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:type\"}}},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:url\"}}},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:card\"}}},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:description\"}}},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:image\"}}},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:site\"}}},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:title\"}}},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.url\"}}},{\"name\":\"request\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"request\"}}},{\"name\":\"response\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"response\"}}},{\"name\":\"spaces\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"spaces\"}}},{\"name\":\"type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"url\"}}},{\"name\":\"utc_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"xss\"}}}]", "timeFieldName": "@timestamp", - "title": "remote:logstash-*" + "title": "ftr-remote:logstash-*" }, "coreMigrationVersion": "8.0.0", - "id": "remote:logstash-*", + "id": "ftr-remote:logstash-*", "migrationVersion": { "index-pattern": "7.11.0" }, @@ -41,7 +41,7 @@ }, "references": [ { - "id": "remote:logstash-*", + "id": "ftr-remote:logstash-*", "name": "kibanaSavedObjectMeta.searchSourceJSON.index", "type": "index-pattern" } diff --git a/test/functional_ccs/apps/discover/_data_view_ccs.ts b/test/functional_ccs/apps/discover/data_view_ccs.ts similarity index 85% rename from test/functional_ccs/apps/discover/_data_view_ccs.ts rename to test/functional_ccs/apps/discover/data_view_ccs.ts index 5a04e0e2a7532..44258b9cbadd6 100644 --- a/test/functional_ccs/apps/discover/_data_view_ccs.ts +++ b/test/functional_ccs/apps/discover/data_view_ccs.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from './ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); - const esArchiver = getService('esArchiver'); + const remoteEsArchiver = getService('remoteEsArchiver'); const security = getService('security'); const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); @@ -30,8 +30,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // FLAKY: https://github.com/elastic/kibana/issues/126658 describe.skip('discover integration with data view editor', function describeIndexTests() { before(async function () { - await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await security.testUser.setRoles([ + 'kibana_admin', + 'test_logstash_reader', + 'ccs_remote_search', + ]); + await remoteEsArchiver.loadIfNeeded( + 'test/functional/fixtures/es_archiver/logstash_functional' + ); await kibanaServer.savedObjects.clean({ types: ['saved-search', 'index-pattern'] }); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); @@ -45,7 +51,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('use ccs to create a new data view', async function () { - const dataViewToCreate = 'remote:logstash'; + const dataViewToCreate = 'ftr-remote:logstash'; await createDataView(dataViewToCreate); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.waitForWithTimeout( diff --git a/test/functional_ccs/apps/discover/index.ts b/test/functional_ccs/apps/discover/index.ts index 629423b1b75aa..2e9d428f44c60 100644 --- a/test/functional_ccs/apps/discover/index.ts +++ b/test/functional_ccs/apps/discover/index.ts @@ -6,38 +6,24 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from './ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, loadTestFile }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); - const esClient = getService('es'); describe('discover app css', function () { this.tags('ciGroup6'); - before(async function () { - await esClient.cluster.putSettings({ - persistent: { - cluster: { - remote: { - remote: { - skip_unavailable: 'true', - seeds: ['localhost:9300'], - }, - }, - }, - }, - }); - return browser.setWindowSize(1300, 800); + before(async () => { + await browser.setWindowSize(1300, 800); }); - after(function unloadMakelogs() { - // Make sure to clean up the cluster setting from the before above. - return esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); - }); + loadTestFile(require.resolve('./data_view_ccs')); + loadTestFile(require.resolve('./saved_queries_ccs')); - loadTestFile(require.resolve('./_data_view_ccs')); - loadTestFile(require.resolve('./_saved_queries_ccs')); + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + }); }); } diff --git a/test/functional_ccs/apps/discover/_saved_queries_ccs.ts b/test/functional_ccs/apps/discover/saved_queries_ccs.ts similarity index 93% rename from test/functional_ccs/apps/discover/_saved_queries_ccs.ts rename to test/functional_ccs/apps/discover/saved_queries_ccs.ts index 325f279ff28ab..08b6d61368f5d 100644 --- a/test/functional_ccs/apps/discover/_saved_queries_ccs.ts +++ b/test/functional_ccs/apps/discover/saved_queries_ccs.ts @@ -8,12 +8,12 @@ import expect from '@kbn/expect'; -import { FtrProviderContext } from './ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const log = getService('log'); - const esArchiver = getService('esArchiver'); + const remoteEsArchiver = getService('remoteEsArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); const browser = getService('browser'); @@ -47,8 +47,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.load( 'test/functional/fixtures/kbn_archiver/date_nested_ccs.json' ); - await esArchiver.load('test/functional/fixtures/es_archiver/date_nested'); - await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); + await remoteEsArchiver.load('test/functional/fixtures/es_archiver/date_nested'); + await remoteEsArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); await kibanaServer.uiSettings.replace(defaultSettings); log.debug('discover'); @@ -61,8 +61,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.unload( 'test/functional/fixtures/kbn_archiver/date_nested_ccs' ); - await esArchiver.unload('test/functional/fixtures/es_archiver/date_nested'); - await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await remoteEsArchiver.unload('test/functional/fixtures/es_archiver/date_nested'); + await remoteEsArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); await PageObjects.common.unsetTime(); }); @@ -87,12 +87,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(false); expect(await queryBar.getQueryString()).to.eql(''); - await PageObjects.discover.selectIndexPattern('remote:date-nested'); + await PageObjects.discover.selectIndexPattern('ftr-remote:date-nested'); expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(false); expect(await queryBar.getQueryString()).to.eql(''); - await PageObjects.discover.selectIndexPattern('remote:logstash-*'); + await PageObjects.discover.selectIndexPattern('ftr-remote:logstash-*'); expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(false); expect(await queryBar.getQueryString()).to.eql(''); diff --git a/test/functional_ccs/config.js b/test/functional_ccs/config.js deleted file mode 100644 index 4cd8875798372..0000000000000 --- a/test/functional_ccs/config.js +++ /dev/null @@ -1,25 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { services } from '../functional/services'; - -export default async function ({ readConfigFile }) { - const functionalConfig = await readConfigFile(require.resolve('../functional/config')); - - return { - ...functionalConfig.getAll(), - - testFiles: [require.resolve('./apps/discover')], - - services, - - junit: { - reportName: 'Kibana CCS Tests', - }, - }; -} diff --git a/test/functional_ccs/config.ts b/test/functional_ccs/config.ts new file mode 100644 index 0000000000000..e99a5310453d9 --- /dev/null +++ b/test/functional_ccs/config.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; +import { services } from './services'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + + return { + ...functionalConfig.getAll(), + + testFiles: [require.resolve('./apps/discover')], + + services, + + junit: { + reportName: 'Kibana CCS Tests', + }, + + security: { + ...functionalConfig.get('security'), + remoteEsRoles: { + ccs_remote_search: { + indices: [ + { + names: ['*'], + privileges: ['read', 'view_index_metadata', 'read_cross_cluster'], + }, + ], + }, + }, + defaultRoles: [...(functionalConfig.get('security.defaultRoles') ?? []), 'ccs_remote_search'], + }, + + esTestCluster: { + ...functionalConfig.get('esTestCluster'), + ccs: { + remoteClusterUrl: + process.env.REMOTE_CLUSTER_URL ?? + `http://elastic:changeme@localhost:${ + functionalConfig.get('servers.elasticsearch.port') + 1 + }`, + }, + }, + }; +} diff --git a/test/functional_ccs/apps/discover/ftr_provider_context.d.ts b/test/functional_ccs/ftr_provider_context.ts similarity index 80% rename from test/functional_ccs/apps/discover/ftr_provider_context.d.ts rename to test/functional_ccs/ftr_provider_context.ts index ea232d23463e6..8fa82b46ac406 100644 --- a/test/functional_ccs/apps/discover/ftr_provider_context.d.ts +++ b/test/functional_ccs/ftr_provider_context.ts @@ -7,7 +7,7 @@ */ import { GenericFtrProviderContext } from '@kbn/test'; -import { services } from '../../../functional/services'; -import { pageObjects } from '../../../functional/page_objects'; +import { services } from './services'; +import { pageObjects } from '../functional/page_objects'; export type FtrProviderContext = GenericFtrProviderContext; diff --git a/test/functional_ccs/services/index.ts b/test/functional_ccs/services/index.ts new file mode 100644 index 0000000000000..dcdffa077fe08 --- /dev/null +++ b/test/functional_ccs/services/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { services as functionalServices } from '../../functional/services'; +import { RemoteEsProvider } from './remote_es'; +import { RemoteEsArchiverProvider } from './remote_es_archiver'; + +export const services = { + ...functionalServices, + remoteEs: RemoteEsProvider, + remoteEsArchiver: RemoteEsArchiverProvider, +}; diff --git a/test/functional_ccs/services/remote_es.ts b/test/functional_ccs/services/remote_es.ts new file mode 100644 index 0000000000000..05a10d9e068f0 --- /dev/null +++ b/test/functional_ccs/services/remote_es.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Client } from '@elastic/elasticsearch'; + +import { systemIndicesSuperuser, createRemoteEsClientForFtrConfig } from '@kbn/test'; +import { FtrProviderContext } from '../ftr_provider_context'; + +/** + * Kibana-specific @elastic/elasticsearch client instance. + */ +export function RemoteEsProvider({ getService }: FtrProviderContext): Client { + const config = getService('config'); + + return createRemoteEsClientForFtrConfig(config, { + // Use system indices user so tests can write to system indices + authOverride: systemIndicesSuperuser, + }); +} diff --git a/test/functional_ccs/services/remote_es_archiver.ts b/test/functional_ccs/services/remote_es_archiver.ts new file mode 100644 index 0000000000000..569792d050a4d --- /dev/null +++ b/test/functional_ccs/services/remote_es_archiver.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EsArchiver } from '@kbn/es-archiver'; +import { FtrProviderContext } from '../ftr_provider_context'; + +export function RemoteEsArchiverProvider({ getService }: FtrProviderContext): EsArchiver { + const remoteEs = getService('remoteEs'); + const log = getService('log'); + const kibanaServer = getService('kibanaServer'); + + return new EsArchiver({ + client: remoteEs, + log, + kbnClient: kibanaServer, + }); +} From 2f83c6516dfa9abf121503f6301d8135d99a6cd4 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Mon, 7 Mar 2022 18:00:49 -0500 Subject: [PATCH 043/140] Fix session cleanup test (#126966) --- .../session_management/session_index.test.ts | 22 +++++++++- .../session_management/session_index.ts | 41 +++++++++++++------ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security/server/session_management/session_index.test.ts b/x-pack/plugins/security/server/session_management/session_index.test.ts index 94e92a2689fc3..b19f580f2d8dd 100644 --- a/x-pack/plugins/security/server/session_management/session_index.test.ts +++ b/x-pack/plugins/security/server/session_management/session_index.test.ts @@ -215,6 +215,7 @@ describe('Session index', () => { expect(mockElasticsearchClient.search).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.bulk).not.toHaveBeenCalled(); expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.refresh).not.toHaveBeenCalled(); // since the search failed, we don't refresh the index }); it('throws if bulk delete call to Elasticsearch fails', async () => { @@ -227,7 +228,20 @@ describe('Session index', () => { expect(mockElasticsearchClient.openPointInTime).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.search).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.bulk).toHaveBeenCalledTimes(1); - expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); // since we attempted to delete sessions, we still refresh the index + }); + + it('does not throw if index refresh call to Elasticsearch fails', async () => { + const failureReason = new errors.ResponseError( + securityMock.createApiResponse(securityMock.createApiResponse({ body: { type: 'Uh oh.' } })) + ); + mockElasticsearchClient.indices.refresh.mockRejectedValue(failureReason); + + await sessionIndex.cleanUp(); + expect(mockElasticsearchClient.openPointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.search).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.bulk).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); // since we attempted to delete sessions, we still refresh the index }); it('when neither `lifespan` nor `idleTimeout` is configured', async () => { @@ -388,6 +402,7 @@ describe('Session index', () => { } ); expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.refresh).toHaveBeenCalledTimes(1); }); it('when only `idleTimeout` is configured', async () => { @@ -474,6 +489,7 @@ describe('Session index', () => { } ); expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.refresh).toHaveBeenCalledTimes(1); }); it('when both `lifespan` and `idleTimeout` are configured', async () => { @@ -570,6 +586,7 @@ describe('Session index', () => { } ); expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.refresh).toHaveBeenCalledTimes(1); }); it('when both `lifespan` and `idleTimeout` are configured and multiple providers are enabled', async () => { @@ -714,6 +731,7 @@ describe('Session index', () => { } ); expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.refresh).toHaveBeenCalledTimes(1); }); it('should clean up sessions in batches of 10,000', async () => { @@ -729,6 +747,7 @@ describe('Session index', () => { expect(mockElasticsearchClient.search).toHaveBeenCalledTimes(2); expect(mockElasticsearchClient.bulk).toHaveBeenCalledTimes(2); expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.refresh).toHaveBeenCalledTimes(1); }); it('should limit number of batches to 10', async () => { @@ -742,6 +761,7 @@ describe('Session index', () => { expect(mockElasticsearchClient.search).toHaveBeenCalledTimes(10); expect(mockElasticsearchClient.bulk).toHaveBeenCalledTimes(10); expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.refresh).toHaveBeenCalledTimes(1); }); it('should log audit event', async () => { diff --git a/x-pack/plugins/security/server/session_management/session_index.ts b/x-pack/plugins/security/server/session_management/session_index.ts index 8a69b9b7f0043..2a677f6ecf561 100644 --- a/x-pack/plugins/security/server/session_management/session_index.ts +++ b/x-pack/plugins/security/server/session_management/session_index.ts @@ -444,20 +444,21 @@ export class SessionIndex { * Trigger a removal of any outdated session values. */ async cleanUp() { - this.options.logger.debug(`Running cleanup routine.`); + const { auditLogger, elasticsearchClient, logger } = this.options; + logger.debug(`Running cleanup routine.`); + let error: Error | undefined; + let indexNeedsRefresh = false; try { for await (const sessionValues of this.getSessionValuesInBatches()) { const operations: Array>> = []; sessionValues.forEach(({ _id, _source }) => { const { usernameHash, provider } = _source!; - this.options.auditLogger.log( - sessionCleanupEvent({ sessionId: _id, usernameHash, provider }) - ); + auditLogger.log(sessionCleanupEvent({ sessionId: _id, usernameHash, provider })); operations.push({ delete: { _id } }); }); if (operations.length > 0) { - const bulkResponse = await this.options.elasticsearchClient.bulk( + const bulkResponse = await elasticsearchClient.bulk( { index: this.indexName, operations, @@ -471,24 +472,40 @@ export class SessionIndex { 0 ); if (errorCount < bulkResponse.items.length) { - this.options.logger.warn( + logger.warn( `Failed to clean up ${errorCount} of ${bulkResponse.items.length} invalid or expired sessions. The remaining sessions were cleaned up successfully.` ); + indexNeedsRefresh = true; } else { - this.options.logger.error( + logger.error( `Failed to clean up ${bulkResponse.items.length} invalid or expired sessions.` ); } } else { - this.options.logger.debug( - `Cleaned up ${bulkResponse.items.length} invalid or expired sessions.` - ); + logger.debug(`Cleaned up ${bulkResponse.items.length} invalid or expired sessions.`); + indexNeedsRefresh = true; } } } } catch (err) { - this.options.logger.error(`Failed to clean up sessions: ${err.message}`); - throw err; + logger.error(`Failed to clean up sessions: ${err.message}`); + error = err; + } + + if (indexNeedsRefresh) { + // Only refresh the index if we have actually deleted one or more sessions. The index will auto-refresh eventually anyway, this just + // ensures that searches after the cleanup process are accurate, and this only impacts integration tests. + try { + await elasticsearchClient.indices.refresh({ index: this.indexName }); + logger.debug(`Refreshed session index.`); + } catch (err) { + logger.error(`Failed to refresh session index: ${err.message}`); + } + } + + if (error) { + // If we couldn't fetch or delete sessions, throw an error so the task will be retried. + throw error; } } From 18a53440825654d98cefa1ca002cc15c1beabdeb Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 7 Mar 2022 19:51:33 -0800 Subject: [PATCH 044/140] [Security Solution][Rules] - Remove rule selection for read only users (#126827) Resolves #126328 and #126314. --- .../missing_privileges_callout.spec.ts | 19 +----- .../all_rules_read_only.spec.ts | 60 +++++++++++++++++++ .../public/detections/pages/alerts/index.tsx | 24 +------- .../detections/pages/alerts/translations.ts | 7 --- .../rules/all/rules_tables.tsx | 2 +- .../public/exceptions/routes.tsx | 10 +++- .../public/exceptions/translations.ts | 15 +++++ .../security_solution/public/rules/routes.tsx | 34 +++++++---- .../public/rules/translations.ts | 15 +++++ .../security_solution/public/translations.ts | 12 ++++ .../public/use_readonly_header.ts | 37 ++++++++++++ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 13 files changed, 175 insertions(+), 62 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/integration/detection_rules/all_rules_read_only.spec.ts create mode 100644 x-pack/plugins/security_solution/public/exceptions/translations.ts create mode 100644 x-pack/plugins/security_solution/public/rules/translations.ts create mode 100644 x-pack/plugins/security_solution/public/translations.ts create mode 100644 x-pack/plugins/security_solution/public/use_readonly_header.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts index 64377c03af6c4..cf74ca338220b 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/missing_privileges_callout.spec.ts @@ -72,24 +72,7 @@ describe('Detections > Callouts', () => { }); }); - context('On Rules Management page', () => { - beforeEach(() => { - loadPageAsReadOnlyUser(DETECTIONS_RULE_MANAGEMENT_URL); - }); - - it('We show one primary callout', () => { - waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); - }); - - context('When a user clicks Dismiss on the callout', () => { - it('We hide it and persist the dismissal', () => { - waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); - dismissCallOut(MISSING_PRIVILEGES_CALLOUT); - reloadPage(); - getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); - }); - }); - }); + // FYI: Rules Management check moved to ../detection_rules/all_rules_read_only.spec.ts context('On Rule Details page', () => { beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/all_rules_read_only.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/all_rules_read_only.spec.ts new file mode 100644 index 0000000000000..845c5c2bca9d6 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/all_rules_read_only.spec.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ROLES } from '../../../common/test'; +import { getNewRule } from '../../objects/rule'; +import { + COLLAPSED_ACTION_BTN, + RULE_CHECKBOX, + RULE_NAME, +} from '../../screens/alerts_detection_rules'; +import { PAGE_TITLE } from '../../screens/common/page'; +import { waitForRulesTableToBeLoaded } from '../../tasks/alerts_detection_rules'; +import { createCustomRule } from '../../tasks/api_calls/rules'; +import { cleanKibana } from '../../tasks/common'; +import { dismissCallOut, getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts'; +import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; +import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation'; + +const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges'; + +describe('All rules - read only', () => { + before(() => { + cleanKibana(); + createCustomRule(getNewRule(), '1'); + loginAndWaitForPageWithoutDateRange(SECURITY_DETECTIONS_RULES_URL, ROLES.reader); + waitForRulesTableToBeLoaded(); + cy.get(RULE_NAME).should('have.text', getNewRule().name); + }); + + it('Does not display select boxes for rules', () => { + cy.get(RULE_CHECKBOX).should('not.exist'); + }); + + it('Does not display action options', () => { + // These are the 3 dots at the end of the row that opens up + // options to take action on the rule + cy.get(COLLAPSED_ACTION_BTN).should('not.exist'); + }); + + it('Displays missing privileges primary callout', () => { + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); + }); + + context('When a user clicks Dismiss on the callouts', () => { + it('We hide them and persist the dismissal', () => { + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); + + dismissCallOut(MISSING_PRIVILEGES_CALLOUT); + cy.reload(); + cy.get(PAGE_TITLE).should('be.visible'); + cy.get(RULE_NAME).should('have.text', getNewRule().name); + + getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/alerts/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/alerts/index.tsx index 18952feee528b..3cf344c691ccd 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/alerts/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/alerts/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { ALERTS_PATH, SecurityPageName } from '../../../../common/constants'; @@ -13,9 +13,8 @@ import { NotFoundPage } from '../../../app/404'; import * as i18n from './translations'; import { TrackApplicationView } from '../../../../../../../src/plugins/usage_collection/public'; import { DetectionEnginePage } from '../../pages/detection_engine/detection_engine'; -import { useKibana } from '../../../common/lib/kibana'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; -import { useAlertsPrivileges } from '../../containers/detection_engine/alerts/use_alerts_privileges'; +import { useReadonlyHeader } from '../../../use_readonly_header'; const AlertsRoute = () => ( @@ -25,24 +24,7 @@ const AlertsRoute = () => ( ); const AlertsContainerComponent: React.FC = () => { - const { chrome } = useKibana().services; - const { hasIndexRead, hasIndexWrite } = useAlertsPrivileges(); - - useEffect(() => { - // if the user is read only then display the glasses badge in the global navigation header - if (!hasIndexWrite && hasIndexRead) { - chrome.setBadge({ - text: i18n.READ_ONLY_BADGE_TEXT, - tooltip: i18n.READ_ONLY_BADGE_TOOLTIP, - iconType: 'glasses', - }); - } - - // remove the icon after the component unmounts - return () => { - chrome.setBadge(); - }; - }, [chrome, hasIndexRead, hasIndexWrite]); + useReadonlyHeader(i18n.READ_ONLY_BADGE_TOOLTIP); return ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/alerts/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/alerts/translations.ts index 734e93925e536..de0b6a5f37d93 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/alerts/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/alerts/translations.ts @@ -7,13 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const READ_ONLY_BADGE_TEXT = i18n.translate( - 'xpack.securitySolution.alerts.badge.readOnly.text', - { - defaultMessage: 'Read only', - } -); - export const READ_ONLY_BADGE_TOOLTIP = i18n.translate( 'xpack.securitySolution.alerts.badge.readOnly.tooltip', { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx index 1b8adb0bd11d3..c38c8e48928f1 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx @@ -390,7 +390,7 @@ export const RulesTables = React.memo( onChange={tableOnChangeCallback} pagination={paginationMemo} ref={tableRef} - selection={euiBasicTableSelectionProps} + selection={hasPermissions ? euiBasicTableSelectionProps : undefined} sorting={{ sort: { // EuiBasicTable has incorrect `sort.field` types which accept only `keyof Item` and reject fields in dot notation diff --git a/x-pack/plugins/security_solution/public/exceptions/routes.tsx b/x-pack/plugins/security_solution/public/exceptions/routes.tsx index a5b95ffa64d4d..262db114ae84e 100644 --- a/x-pack/plugins/security_solution/public/exceptions/routes.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/routes.tsx @@ -7,11 +7,13 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; +import * as i18n from './translations'; import { TrackApplicationView } from '../../../../../src/plugins/usage_collection/public'; import { EXCEPTIONS_PATH, SecurityPageName } from '../../common/constants'; import { ExceptionListsTable } from '../detections/pages/detection_engine/rules/all/exceptions/exceptions_table'; import { SpyRoute } from '../common/utils/route/spy_routes'; import { NotFoundPage } from '../app/404'; +import { useReadonlyHeader } from '../use_readonly_header'; const ExceptionsRoutes = () => { return ( @@ -22,7 +24,9 @@ const ExceptionsRoutes = () => { ); }; -const renderExceptionsRoutes = () => { +const ExceptionsContainerComponent: React.FC = () => { + useReadonlyHeader(i18n.READ_ONLY_BADGE_TOOLTIP); + return ( @@ -31,6 +35,10 @@ const renderExceptionsRoutes = () => { ); }; +const Exceptions = React.memo(ExceptionsContainerComponent); + +const renderExceptionsRoutes = () => ; + export const routes = [ { path: EXCEPTIONS_PATH, diff --git a/x-pack/plugins/security_solution/public/exceptions/translations.ts b/x-pack/plugins/security_solution/public/exceptions/translations.ts new file mode 100644 index 0000000000000..780ed23a64ffe --- /dev/null +++ b/x-pack/plugins/security_solution/public/exceptions/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const READ_ONLY_BADGE_TOOLTIP = i18n.translate( + 'xpack.securitySolution.exceptions.badge.readOnly.tooltip', + { + defaultMessage: 'Unable to create, edit or delete exceptions', + } +); diff --git a/x-pack/plugins/security_solution/public/rules/routes.tsx b/x-pack/plugins/security_solution/public/rules/routes.tsx index fcb434ae760ed..4172e75a3cd96 100644 --- a/x-pack/plugins/security_solution/public/rules/routes.tsx +++ b/x-pack/plugins/security_solution/public/rules/routes.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; +import * as i18n from './translations'; import { TrackApplicationView } from '../../../../../src/plugins/usage_collection/public'; import { RULES_PATH, SecurityPageName } from '../../common/constants'; import { NotFoundPage } from '../app/404'; @@ -14,6 +15,7 @@ import { RulesPage } from '../detections/pages/detection_engine/rules'; import { CreateRulePage } from '../detections/pages/detection_engine/rules/create'; import { RuleDetailsPage } from '../detections/pages/detection_engine/rules/details'; import { EditRulePage } from '../detections/pages/detection_engine/rules/edit'; +import { useReadonlyHeader } from '../use_readonly_header'; const RulesSubRoutes = [ { @@ -38,18 +40,26 @@ const RulesSubRoutes = [ }, ]; -const renderRulesRoutes = () => ( - - - {RulesSubRoutes.map((route, index) => ( - - - - ))} - - - -); +const RulesContainerComponent: React.FC = () => { + useReadonlyHeader(i18n.READ_ONLY_BADGE_TOOLTIP); + + return ( + + + {RulesSubRoutes.map((route, index) => ( + + + + ))} + + + + ); +}; + +const Rules = React.memo(RulesContainerComponent); + +const renderRulesRoutes = () => ; export const routes = [ { diff --git a/x-pack/plugins/security_solution/public/rules/translations.ts b/x-pack/plugins/security_solution/public/rules/translations.ts new file mode 100644 index 0000000000000..2d2c5de70dba9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/rules/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const READ_ONLY_BADGE_TOOLTIP = i18n.translate( + 'xpack.securitySolution.rules.badge.readOnly.tooltip', + { + defaultMessage: 'Unable to create, edit or delete rules', + } +); diff --git a/x-pack/plugins/security_solution/public/translations.ts b/x-pack/plugins/security_solution/public/translations.ts new file mode 100644 index 0000000000000..18c6ceea1f837 --- /dev/null +++ b/x-pack/plugins/security_solution/public/translations.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const READ_ONLY_BADGE_TEXT = i18n.translate('xpack.securitySolution.badge.readOnly.text', { + defaultMessage: 'Read only', +}); diff --git a/x-pack/plugins/security_solution/public/use_readonly_header.ts b/x-pack/plugins/security_solution/public/use_readonly_header.ts new file mode 100644 index 0000000000000..d48855b397105 --- /dev/null +++ b/x-pack/plugins/security_solution/public/use_readonly_header.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect } from 'react'; + +import * as i18n from './translations'; +import { useKibana } from './common/lib/kibana'; +import { useAlertsPrivileges } from './detections/containers/detection_engine/alerts/use_alerts_privileges'; + +/** + * This component places a read-only icon badge in the header + * if user only has read *Kibana* privileges, not individual data index + * privileges + */ +export function useReadonlyHeader(tooltip: string) { + const { hasKibanaREAD, hasKibanaCRUD } = useAlertsPrivileges(); + const chrome = useKibana().services.chrome; + + useEffect(() => { + if (hasKibanaREAD && !hasKibanaCRUD) { + chrome.setBadge({ + text: i18n.READ_ONLY_BADGE_TEXT, + tooltip, + iconType: 'glasses', + }); + } + + // remove the icon after the component unmounts + return () => { + chrome.setBadge(); + }; + }, [chrome, hasKibanaREAD, hasKibanaCRUD, tooltip]); +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3df1c0a71b095..8a39c36c870d4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22651,7 +22651,6 @@ "xpack.securitySolution.alertDetails.summary.readLess": "表示を減らす", "xpack.securitySolution.alertDetails.summary.readMore": "続きを読む", "xpack.securitySolution.alertDetails.threatIntel": "Threat Intel", - "xpack.securitySolution.alerts.badge.readOnly.text": "読み取り専用", "xpack.securitySolution.alerts.badge.readOnly.tooltip": "アラートを更新できません", "xpack.securitySolution.alerts.riskScoreMapping.defaultDescriptionLabel": "このルールで生成されたすべてのアラートのリスクスコアを選択します。", "xpack.securitySolution.alerts.riskScoreMapping.defaultRiskScoreTitle": "デフォルトリスクスコア", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0326413eb0ac8..bdba6f9225cc0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22680,7 +22680,6 @@ "xpack.securitySolution.alertDetails.summary.readLess": "阅读更少内容", "xpack.securitySolution.alertDetails.summary.readMore": "阅读更多内容", "xpack.securitySolution.alertDetails.threatIntel": "威胁情报", - "xpack.securitySolution.alerts.badge.readOnly.text": "只读", "xpack.securitySolution.alerts.badge.readOnly.tooltip": "无法更新告警", "xpack.securitySolution.alerts.riskScoreMapping.defaultDescriptionLabel": "选择此规则生成的所有告警的风险分数。", "xpack.securitySolution.alerts.riskScoreMapping.defaultRiskScoreTitle": "默认风险分数", From 50ba761d1402014dee4ef866c2ebf4fec2ca0740 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 7 Mar 2022 20:19:26 -0800 Subject: [PATCH 045/140] [Security Solution][Lists] - Updates exception flyout edit error messages (#126875) Addresses #126669 Error messages on flyout were being truncated/were out of view. --- .../exceptions/exceptions_flyout.spec.ts | 134 ++++++++++++++++-- .../cypress/objects/exception.ts | 11 ++ .../cypress/screens/exceptions.ts | 9 ++ .../cypress/screens/rule_details.ts | 2 + .../cypress/tasks/api_calls/exceptions.ts | 57 +++++++- .../cypress/tasks/rule_details.ts | 7 +- .../edit_exception_flyout/index.tsx | 54 ++++--- 7 files changed, 236 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts index d68def1284468..60202a4f6a52a 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_flyout.spec.ts @@ -29,10 +29,21 @@ import { EXCEPTION_ITEM_CONTAINER, ADD_EXCEPTIONS_BTN, EXCEPTION_FIELD_LIST, + EDIT_EXCEPTIONS_BTN, + EXCEPTION_EDIT_FLYOUT_SAVE_BTN, + EXCEPTION_FLYOUT_VERSION_CONFLICT, + EXCEPTION_FLYOUT_LIST_DELETED_ERROR, } from '../../screens/exceptions'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; import { cleanKibana, reload } from '../../tasks/common'; +import { + createExceptionList, + createExceptionListItem, + updateExceptionListItem, + deleteExceptionList, +} from '../../tasks/api_calls/exceptions'; +import { getExceptionList } from '../../objects/exception'; // NOTE: You might look at these tests and feel they're overkill, // but the exceptions flyout has a lot of logic making it difficult @@ -42,18 +53,28 @@ import { cleanKibana, reload } from '../../tasks/common'; describe('Exceptions flyout', () => { before(() => { cleanKibana(); - loginAndWaitForPageWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); - createCustomRule({ ...getNewRule(), index: ['exceptions-*'] }); - reload(); - goToRuleDetails(); - - cy.get(RULE_STATUS).should('have.text', '—'); - // this is a made-up index that has just the necessary // mappings to conduct tests, avoiding loading large // amounts of data like in auditbeat_exceptions esArchiverLoad('exceptions'); - + loginAndWaitForPageWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL); + createExceptionList(getExceptionList(), getExceptionList().list_id).then((response) => + createCustomRule({ + ...getNewRule(), + index: ['exceptions-*'], + exceptionLists: [ + { + id: response.body.id, + list_id: getExceptionList().list_id, + type: getExceptionList().type, + namespace_type: getExceptionList().namespace_type, + }, + ], + }) + ); + reload(); + goToRuleDetails(); + cy.get(RULE_STATUS).should('have.text', '—'); goToExceptionsTab(); }); @@ -62,7 +83,12 @@ describe('Exceptions flyout', () => { }); it('Does not overwrite values and-ed together', () => { - cy.get(ADD_EXCEPTIONS_BTN).click({ force: true }); + cy.root() + .pipe(($el) => { + $el.find(ADD_EXCEPTIONS_BTN).trigger('click'); + return $el.find(ADD_AND_BTN); + }) + .should('be.visible'); // add multiple entries with invalid field values addExceptionEntryFieldValue('agent.name', 0); @@ -80,8 +106,12 @@ describe('Exceptions flyout', () => { }); it('Does not overwrite values or-ed together', () => { - cy.get(ADD_EXCEPTIONS_BTN).click({ force: true }); - + cy.root() + .pipe(($el) => { + $el.find(ADD_EXCEPTIONS_BTN).trigger('click'); + return $el.find(ADD_AND_BTN); + }) + .should('be.visible'); // exception item 1 addExceptionEntryFieldValueOfItemX('agent.name', 0, 0); cy.get(ADD_AND_BTN).click(); @@ -197,11 +227,89 @@ describe('Exceptions flyout', () => { }); it('Contains custom index fields', () => { - cy.get(ADD_EXCEPTIONS_BTN).click({ force: true }); - + cy.root() + .pipe(($el) => { + $el.find(ADD_EXCEPTIONS_BTN).trigger('click'); + return $el.find(ADD_AND_BTN); + }) + .should('be.visible'); cy.get(FIELD_INPUT).eq(0).click({ force: true }); cy.get(EXCEPTION_FIELD_LIST).contains('unique_value.test'); closeExceptionBuilderFlyout(); }); + + describe('flyout errors', () => { + before(() => { + // create exception item via api + createExceptionListItem(getExceptionList().list_id, { + list_id: getExceptionList().list_id, + item_id: 'simple_list_item', + tags: [], + type: 'simple', + description: 'Test exception item', + name: 'Sample Exception List Item', + namespace_type: 'single', + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host', 'another host'], + }, + ], + }); + + reload(); + cy.get(RULE_STATUS).should('have.text', '—'); + goToExceptionsTab(); + }); + + context('When updating an item with version conflict', () => { + it('Displays version conflict error', () => { + cy.get(EDIT_EXCEPTIONS_BTN).should('be.visible'); + cy.get(EDIT_EXCEPTIONS_BTN).click({ force: true }); + + // update exception item via api + updateExceptionListItem('simple_list_item', { + name: 'Updated item name', + item_id: 'simple_list_item', + tags: [], + type: 'simple', + description: 'Test exception item', + namespace_type: 'single', + entries: [ + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host', 'another host'], + }, + ], + }); + + // try to save and see version conflict error + cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).click({ force: true }); + + cy.get(EXCEPTION_FLYOUT_VERSION_CONFLICT).should('be.visible'); + + closeExceptionBuilderFlyout(); + }); + }); + + context('When updating an item for a list that has since been deleted', () => { + it('Displays missing exception list error', () => { + cy.get(EDIT_EXCEPTIONS_BTN).should('be.visible'); + cy.get(EDIT_EXCEPTIONS_BTN).click({ force: true }); + + // delete exception list via api + deleteExceptionList(getExceptionList().list_id, getExceptionList().namespace_type); + + // try to save and see error + cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).click({ force: true }); + + cy.get(EXCEPTION_FLYOUT_LIST_DELETED_ERROR).should('be.visible'); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/exception.ts b/x-pack/plugins/security_solution/cypress/objects/exception.ts index fbb000f43fdd2..637adc9fc0134 100644 --- a/x-pack/plugins/security_solution/cypress/objects/exception.ts +++ b/x-pack/plugins/security_solution/cypress/objects/exception.ts @@ -22,6 +22,17 @@ export interface ExceptionList { type: 'detection' | 'endpoint'; } +export interface ExceptionListItem { + description: string; + list_id: string; + item_id: string; + name: string; + namespace_type: 'single' | 'agnostic'; + tags: string[]; + type: 'simple'; + entries: Array<{ field: string; operator: string; type: string; value: string[] }>; +} + export const getExceptionList = (): ExceptionList => ({ description: 'Test exception list description', list_id: 'test_exception_list', diff --git a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts index e1b9e0639dfaa..c94c2be8b976f 100644 --- a/x-pack/plugins/security_solution/cypress/screens/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/exceptions.ts @@ -5,6 +5,8 @@ * 2.0. */ +export const EDIT_EXCEPTIONS_BTN = '[data-test-subj="exceptionsViewerEditBtn"]'; + export const ADD_EXCEPTIONS_BTN = '[data-test-subj="exceptionsHeaderAddExceptionBtn"]'; export const CLOSE_ALERTS_CHECKBOX = @@ -61,3 +63,10 @@ export const EXCEPTION_FIELD_LIST = '[data-test-subj="comboBoxOptionsList fieldAutocompleteComboBox-optionsList"]'; export const EXCEPTION_FLYOUT_TITLE = '[data-test-subj="exception-flyout-title"]'; + +export const EXCEPTION_EDIT_FLYOUT_SAVE_BTN = '[data-test-subj="edit-exception-confirm-button"]'; + +export const EXCEPTION_FLYOUT_VERSION_CONFLICT = + '[data-test-subj="exceptionsFlyoutVersionConflict"]'; + +export const EXCEPTION_FLYOUT_LIST_DELETED_ERROR = '[data-test-subj="errorCalloutContainer"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index a057f27df4280..a9134e5b124eb 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -33,6 +33,8 @@ export const DETAILS_TITLE = '.euiDescriptionList__title'; export const EXCEPTIONS_TAB = '[data-test-subj="exceptionsTab"]'; +export const EXCEPTIONS_TAB_SEARCH = '[data-test-subj="exceptionsHeaderSearch"]'; + export const FALSE_POSITIVES_DETAILS = 'False positive examples'; export const INDEX_PATTERNS_DETAILS = 'Index patterns'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/exceptions.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/exceptions.ts index 7363bd5991b1c..ab6c649c7c61f 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/exceptions.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/exceptions.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ExceptionList } from '../../objects/exception'; +import { ExceptionList, ExceptionListItem } from '../../objects/exception'; export const createExceptionList = ( exceptionList: ExceptionList, @@ -23,3 +23,58 @@ export const createExceptionList = ( headers: { 'kbn-xsrf': 'cypress-creds' }, failOnStatusCode: false, }); + +export const createExceptionListItem = ( + exceptionListId: string, + exceptionListItem?: ExceptionListItem +) => + cy.request({ + method: 'POST', + url: '/api/exception_lists/items', + body: { + list_id: exceptionListItem?.list_id ?? exceptionListId, + item_id: exceptionListItem?.item_id ?? 'simple_list_item', + tags: exceptionListItem?.tags ?? ['user added string for a tag', 'malware'], + type: exceptionListItem?.type ?? 'simple', + description: exceptionListItem?.description ?? 'This is a sample endpoint type exception', + name: exceptionListItem?.name ?? 'Sample Exception List Item', + entries: exceptionListItem?.entries ?? [ + { + field: 'actingProcess.file.signer', + operator: 'excluded', + type: 'exists', + }, + { + field: 'host.name', + operator: 'included', + type: 'match_any', + value: ['some host', 'another host'], + }, + ], + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); + +export const updateExceptionListItem = ( + exceptionListItemId: string, + exceptionListItemUpdate?: Partial +) => + cy.request({ + method: 'PUT', + url: '/api/exception_lists/items', + body: { + item_id: exceptionListItemId, + ...exceptionListItemUpdate, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); + +export const deleteExceptionList = (listId: string, namespaceType: string) => + cy.request({ + method: 'DELETE', + url: `/api/exception_lists?list_id=${listId}&namespace_type=${namespaceType}`, + headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, + }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index e69c217c4a764..d42ebcf9da68e 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -91,7 +91,12 @@ export const goToAlertsTab = () => { }; export const goToExceptionsTab = () => { - cy.get(EXCEPTIONS_TAB).click(); + cy.root() + .pipe(($el) => { + $el.find(EXCEPTIONS_TAB).trigger('click'); + return $el.find(ADD_EXCEPTIONS_BTN); + }) + .should('be.visible'); }; export const removeException = () => { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx index 6d01908732ec1..0ed810b4ad263 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx @@ -410,27 +410,35 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ )} - {updateError != null && ( - - - - )} - {hasVersionConflict && ( - - -

{i18n.VERSION_CONFLICT_ERROR_DESCRIPTION}

-
-
- )} - {updateError == null && ( - + + + {hasVersionConflict && ( + <> + +

{i18n.VERSION_CONFLICT_ERROR_DESCRIPTION}

+
+ + + )} + {updateError != null && ( + <> + + + + )} + {updateError === null && ( {i18n.CANCEL} @@ -446,8 +454,8 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ {i18n.EDIT_EXCEPTION_SAVE_BUTTON} -
- )} + )} +
); }); From e86d7aef80e837cdbf0fb78cdf0cda8a573c499d Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 7 Mar 2022 21:32:51 -0800 Subject: [PATCH 046/140] [Security Solution][Lists] - Add missing privileges callout to exception lists page (#126874) Resolves #126330 --- .../all_exception_lists_read_only.spec.ts | 50 +++++++++++++++++++ .../rules/all/exceptions/exceptions_table.tsx | 2 + 2 files changed, 52 insertions(+) create mode 100644 x-pack/plugins/security_solution/cypress/integration/exceptions/all_exception_lists_read_only.spec.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/all_exception_lists_read_only.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/all_exception_lists_read_only.spec.ts new file mode 100644 index 0000000000000..e332019f2754e --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/all_exception_lists_read_only.spec.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ROLES } from '../../../common/test'; +import { getExceptionList } from '../../objects/exception'; +import { EXCEPTIONS_TABLE_SHOWING_LISTS } from '../../screens/exceptions'; +import { createExceptionList } from '../../tasks/api_calls/exceptions'; +import { cleanKibana } from '../../tasks/common'; +import { dismissCallOut, getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts'; +import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; +import { EXCEPTIONS_URL } from '../../urls/navigation'; + +const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges'; + +describe('All exception lists - read only', () => { + before(() => { + cleanKibana(); + + // Create exception list not used by any rules + createExceptionList(getExceptionList(), getExceptionList().list_id); + + loginAndWaitForPageWithoutDateRange(EXCEPTIONS_URL, ROLES.reader); + + cy.reload(); + + // Using cy.contains because we do not care about the exact text, + // just checking number of lists shown + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1'); + }); + + it('Displays missing privileges primary callout', () => { + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); + }); + + context('When a user clicks Dismiss on the callouts', () => { + it('We hide them and persist the dismissal', () => { + waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary'); + + dismissCallOut(MISSING_PRIVILEGES_CALLOUT); + cy.reload(); + cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1'); + + getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx index c40b6b9571724..65684a7c7d9de 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx @@ -39,6 +39,7 @@ import { useUserData } from '../../../../../components/user_info'; import { userHasPermissions } from '../../helpers'; import { useListsConfig } from '../../../../../containers/detection_engine/lists/use_lists_config'; import { ExceptionsTableItem } from './types'; +import { MissingPrivilegesCallOut } from '../../../../../components/callouts/missing_privileges_callout'; export type Func = () => Promise; @@ -349,6 +350,7 @@ export const ExceptionListsTable = React.memo(() => { return ( <> + Date: Tue, 8 Mar 2022 09:05:50 +0200 Subject: [PATCH 047/140] [Lens] Fixed flakiness on runtime fields' appearance on the list (#126945) --- x-pack/test/functional/apps/lens/runtime_fields.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/test/functional/apps/lens/runtime_fields.ts b/x-pack/test/functional/apps/lens/runtime_fields.ts index 2ea0a05aece26..dec66fc20caef 100644 --- a/x-pack/test/functional/apps/lens/runtime_fields.ts +++ b/x-pack/test/functional/apps/lens/runtime_fields.ts @@ -25,6 +25,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.enableValue(); await fieldEditor.typeScript("emit('abc')"); await fieldEditor.save(); + await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.lens.searchField('runtime'); await PageObjects.lens.waitForField('runtimefield'); await PageObjects.lens.dragFieldToWorkspace('runtimefield'); From fc3aedcf7832f98d269b0450e6e084e637a7f63b Mon Sep 17 00:00:00 2001 From: Mat Schaffer Date: Tue, 8 Mar 2022 16:58:08 +0900 Subject: [PATCH 048/140] Hide Enterprise search panel when no nodes are present (#127100) --- .../overview/enterprise_search_panel.js | 8 ++++- .../apps/monitoring/cluster/overview.js | 14 ++------- .../services/monitoring/cluster_overview.js | 29 ++++++++++++------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/enterprise_search_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/enterprise_search_panel.js index 1459ccb2ecac6..b8ff05213c1c0 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/enterprise_search_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/enterprise_search_panel.js @@ -31,6 +31,12 @@ import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link' export function EnterpriseSearchPanel(props) { const { setupMode } = props; const setupModeData = get(setupMode.data, 'enterprise_search'); + const nodesCount = props.stats.totalInstances || 0; + + // Do not show if we are not in setup mode + if (!nodesCount && !setupMode.enabled) { + return null; + } return ( diff --git a/x-pack/test/functional/apps/monitoring/cluster/overview.js b/x-pack/test/functional/apps/monitoring/cluster/overview.js index 25e52535a39b2..d4c646d48b92d 100644 --- a/x-pack/test/functional/apps/monitoring/cluster/overview.js +++ b/x-pack/test/functional/apps/monitoring/cluster/overview.js @@ -112,13 +112,9 @@ export default function ({ getService, getPageObjects }) { expect(await overview.getKbnConnections()).to.be('174'); expect(await overview.getKbnMemoryUsage()).to.be('15.33%\n219.6 MB / 1.4 GB'); }); - - it('does not show logstash panel', async () => { - expect(await overview.doesLsPanelExist()).to.be(false); - }); }); - describe('for Yellow cluster with Basic license and no Kibana and Logstash', () => { + describe('for Yellow cluster with Basic license and no other stack components', () => { const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects); before(async () => { @@ -156,12 +152,8 @@ export default function ({ getService, getPageObjects }) { expect(await overview.getEsReplicaShards()).to.be('0'); }); - it('shows kibana panel', async () => { - expect(await overview.doesKbnPanelExist()).to.be(false); - }); - - it('does not show logstash panel', async () => { - expect(await overview.doesLsPanelExist()).to.be(false); + it('shows only elasticsearch panel', async () => { + expect(await overview.getPresentPanels()).to.eql(['Elasticsearch']); }); }); diff --git a/x-pack/test/functional/services/monitoring/cluster_overview.js b/x-pack/test/functional/services/monitoring/cluster_overview.js index e6c58ba4eac03..ff146c94de732 100644 --- a/x-pack/test/functional/services/monitoring/cluster_overview.js +++ b/x-pack/test/functional/services/monitoring/cluster_overview.js @@ -10,11 +10,14 @@ import expect from '@kbn/expect'; export function MonitoringClusterOverviewProvider({ getService }) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); + const find = getService('find'); const SUBJ_CLUSTER_ALERTS = `clusterAlertsContainer`; const SUBJ_CLUSTER_NAME = `overviewTabsclusterName`; - const SUBJ_ES_PANEL = `clusterItemContainerElasticsearch`; + const SUBJ_CLUSTER_ITEM_CONTAINER_PREFIX = `clusterItemContainer`; + + const SUBJ_ES_PANEL = `${SUBJ_CLUSTER_ITEM_CONTAINER_PREFIX}Elasticsearch`; const SUBJ_ES_STATUS = `${SUBJ_ES_PANEL} > statusIcon`; const SUBJ_ES_VERSION = `${SUBJ_ES_PANEL} > esVersion`; const SUBJ_ES_UPTIME = `${SUBJ_ES_PANEL} > esUptime`; @@ -29,7 +32,7 @@ export function MonitoringClusterOverviewProvider({ getService }) { const SUBJ_ES_REPLICA_SHARDS = `${SUBJ_ES_PANEL} > esReplicaShards`; const SUBJ_ES_ML_JOBS = `${SUBJ_ES_PANEL} > esMlJobs`; - const SUBJ_KBN_PANEL = `clusterItemContainerKibana`; + const SUBJ_KBN_PANEL = `${SUBJ_CLUSTER_ITEM_CONTAINER_PREFIX}Kibana`; const SUBJ_KBN_STATUS = `${SUBJ_KBN_PANEL} > statusIcon`; const SUBJ_KBN_REQUESTS = `${SUBJ_KBN_PANEL} > kbnRequests`; const SUBJ_KBN_MAX_RESPONSE_TIME = `${SUBJ_KBN_PANEL} > kbnMaxResponseTime`; @@ -38,7 +41,7 @@ export function MonitoringClusterOverviewProvider({ getService }) { const SUBJ_KBN_OVERVIEW = `${SUBJ_KBN_PANEL} > kbnOverview`; const SUBJ_KBN_INSTANCES = `${SUBJ_KBN_PANEL} > kbnInstances`; - const SUBJ_LS_PANEL = `clusterItemContainerLogstash`; + const SUBJ_LS_PANEL = `${SUBJ_CLUSTER_ITEM_CONTAINER_PREFIX}Logstash`; const SUBJ_LS_EVENTS_RECEIVED = `${SUBJ_LS_PANEL} > lsEventsReceived`; const SUBJ_LS_EVENTS_EMITTED = `${SUBJ_LS_PANEL} > lsEventsEmitted`; const SUBJ_LS_NODES = `${SUBJ_LS_PANEL} > lsNodes`; @@ -47,14 +50,14 @@ export function MonitoringClusterOverviewProvider({ getService }) { const SUBJ_LS_PIPELINES = `${SUBJ_LS_PANEL} > lsPipelines`; const SUBJ_LS_OVERVIEW = `${SUBJ_LS_PANEL} > lsOverview`; - const SUBJ_BEATS_PANEL = `clusterItemContainerBeats`; + const SUBJ_BEATS_PANEL = `${SUBJ_CLUSTER_ITEM_CONTAINER_PREFIX}Beats`; const SUBJ_BEATS_OVERVIEW = `${SUBJ_BEATS_PANEL} > beatsOverview`; const SUBJ_BEATS_TOTAL_EVENTS = `${SUBJ_BEATS_PANEL} > beatsTotalEvents`; const SUBJ_BEATS_BYTES_SENT = `${SUBJ_BEATS_PANEL} > beatsBytesSent`; const SUBJ_BEATS_LISTING = `${SUBJ_BEATS_PANEL} > beatsListing`; const SUBJ_BEATS_TYPES_COUNTS = `${SUBJ_BEATS_PANEL} > beatTypeCount`; - const SUBJ_ENT_SEARCH_PANEL = `clusterItemContainerEnterprise Search`; + const SUBJ_ENT_SEARCH_PANEL = `${SUBJ_CLUSTER_ITEM_CONTAINER_PREFIX}Enterprise Search`; const SUBJ_ENT_SEARCH_TOTAL_NODES = `${SUBJ_ENT_SEARCH_PANEL} > entSearchTotalNodes`; const SUBJ_ENT_SEARCH_OVERVIEW = `${SUBJ_ENT_SEARCH_PANEL} > entSearchOverview`; const SUBJ_ENT_SEARCH_ENGINES = `${SUBJ_ENT_SEARCH_PANEL} > appSearchEngines`; @@ -89,6 +92,16 @@ export function MonitoringClusterOverviewProvider({ getService }) { return testSubjects.click('alerts-modal-button'); } + async getPresentPanels() { + const panelElements = await find.allByCssSelector( + `[data-test-subj^="${SUBJ_CLUSTER_ITEM_CONTAINER_PREFIX}"]` + ); + const panelTestSubjects = await Promise.all( + panelElements.map((e) => e.getAttribute('data-test-subj')) + ); + return panelTestSubjects.map((e) => e.replace(SUBJ_CLUSTER_ITEM_CONTAINER_PREFIX, '')); + } + getEsStatus() { return testSubjects.getVisibleText(SUBJ_ES_STATUS); } @@ -140,9 +153,6 @@ export function MonitoringClusterOverviewProvider({ getService }) { return testSubjects.getVisibleText(SUBJ_ES_ML_JOBS); } - doesKbnPanelExist() { - return testSubjects.exists(SUBJ_KBN_PANEL); - } getKbnStatus() { return testSubjects.getVisibleText(SUBJ_KBN_STATUS); } @@ -168,9 +178,6 @@ export function MonitoringClusterOverviewProvider({ getService }) { return testSubjects.click(SUBJ_KBN_INSTANCES); } - doesLsPanelExist() { - return testSubjects.exists(SUBJ_LS_PANEL); - } getLsEventsReceived() { return testSubjects.getVisibleText(SUBJ_LS_EVENTS_RECEIVED); } From 5ad355e8c7b6d132a91adecc2ea3eb84142c72dd Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Tue, 8 Mar 2022 09:01:09 +0100 Subject: [PATCH 049/140] Remove all cases related code from timelines (#127003) --- .../common/mock/mock_timelines_plugin.tsx | 14 -- .../body/events/event_column_view.test.tsx | 11 - .../components/timeline/body/index.test.tsx | 4 - .../public/components/actions/index.ts | 8 - .../cases/add_to_case_action.test.tsx | 195 ----------------- .../timeline/cases/add_to_case_action.tsx | 147 ------------- .../cases/add_to_case_action_button.tsx | 109 ---------- .../cases/add_to_existing_case_button.tsx | 76 ------- .../timeline/cases/add_to_new_case_button.tsx | 77 ------- .../actions/timeline/cases/helpers.test.tsx | 45 ---- .../actions/timeline/cases/helpers.tsx | 43 ---- .../actions/timeline/cases/index.tsx | 11 - .../timeline/cases/toaster_content.test.tsx | 46 ---- .../timeline/cases/toaster_content.tsx | 47 ---- .../actions/timeline/cases/translations.ts | 75 ------- .../components/actions/timeline/index.tsx | 8 - .../components/t_grid/standalone/index.tsx | 37 +--- .../public/hooks/use_add_to_case.test.ts | 71 ------- .../timelines/public/hooks/use_add_to_case.ts | 200 ------------------ .../timelines/public/methods/index.tsx | 76 ------- .../timelines/public/mock/global_state.ts | 2 - .../public/mock/mock_timeline_data.ts | 2 - .../timelines/public/mock/plugin_mock.tsx | 2 - x-pack/plugins/timelines/public/plugin.ts | 36 ---- .../timelines/public/store/t_grid/actions.ts | 8 - .../timelines/public/store/t_grid/model.ts | 2 - .../timelines/public/store/t_grid/reducer.ts | 22 -- .../public/store/t_grid/selectors.ts | 17 +- x-pack/plugins/timelines/public/types.ts | 5 - .../translations/translations/ja-JP.json | 10 - .../translations/translations/zh-CN.json | 10 - .../applications/timelines_test/index.tsx | 6 - 32 files changed, 2 insertions(+), 1420 deletions(-) delete mode 100644 x-pack/plugins/timelines/public/components/actions/index.ts delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action_button.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_new_case_button.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/helpers.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/helpers.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/index.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/toaster_content.test.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/toaster_content.tsx delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/cases/translations.ts delete mode 100644 x-pack/plugins/timelines/public/components/actions/timeline/index.tsx delete mode 100644 x-pack/plugins/timelines/public/hooks/use_add_to_case.test.ts delete mode 100644 x-pack/plugins/timelines/public/hooks/use_add_to_case.ts diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_timelines_plugin.tsx b/x-pack/plugins/security_solution/public/common/mock/mock_timelines_plugin.tsx index 9a75776304349..e2b92b0eeae41 100644 --- a/x-pack/plugins/security_solution/public/common/mock/mock_timelines_plugin.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/mock_timelines_plugin.tsx @@ -15,18 +15,4 @@ export const mockTimelines = { onBlur: jest.fn(), onKeyDown: jest.fn(), }), - getAddToCasePopover: jest - .fn() - .mockReturnValue(
{'Add to case'}
), - getAddToCaseAction: jest.fn(), - getAddToExistingCaseButton: jest.fn().mockReturnValue( -
- {'Add to existing case'} -
- ), - getAddToNewCaseButton: jest.fn().mockReturnValue( -
- {'Add to new case'} -
- ), }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx index 890175ac8daf9..ac4a09c01cc06 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx @@ -52,17 +52,6 @@ jest.mock('../../../../../common/lib/kibana', () => ({ useGetUserCasesPermissions: jest.fn(), })); -jest.mock( - '../../../../../../../timelines/public/components/actions/timeline/cases/add_to_case_action', - () => { - return { - AddToCasePopover: () => { - return
{'Add to case'}
; - }, - }; - } -); - describe('EventColumnView', () => { useIsExperimentalFeatureEnabledMock.mockReturnValue(false); (useShallowEqualSelector as jest.Mock).mockReturnValue(TimelineType.default); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index 5a9f981988d59..bcb4c01fa409f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -61,10 +61,6 @@ jest.mock('../../../../common/lib/kibana', () => { onBlur: jest.fn(), onKeyDown: jest.fn(), }), - getAddToCasePopover: jest - .fn() - .mockReturnValue(
{'Add to case'}
), - getAddToCaseAction: jest.fn(), }, }, }), diff --git a/x-pack/plugins/timelines/public/components/actions/index.ts b/x-pack/plugins/timelines/public/components/actions/index.ts deleted file mode 100644 index 9464a33082a49..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/index.ts +++ /dev/null @@ -1,8 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './timeline'; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.test.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.test.tsx deleted file mode 100644 index 80ae1cfa44465..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.test.tsx +++ /dev/null @@ -1,195 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { - TestProviders, - mockGetAllCasesSelectorModal, - mockGetCreateCaseFlyout, -} from '../../../../mock'; -import { AddToCaseAction } from './add_to_case_action'; -import { SECURITY_SOLUTION_OWNER } from '../../../../../../cases/common'; -import { AddToCaseActionButton } from './add_to_case_action_button'; -import { ALERT_RULE_UUID } from '@kbn/rule-data-utils'; - -jest.mock('react-router-dom', () => ({ - useLocation: () => ({ - search: '', - }), -})); -jest.mock('./helpers'); - -describe('AddToCaseAction', () => { - const props = { - event: { - _id: 'test-id', - data: [], - ecs: { - _id: 'test-id', - _index: 'test-index', - signal: { rule: { id: ['rule-id'], name: ['rule-name'], false_positives: [] } }, - }, - }, - casePermissions: { - crud: true, - read: true, - }, - appId: 'securitySolutionUI', - owner: 'securitySolution', - onClose: () => null, - }; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('it renders', () => { - const wrapper = mount( - - - - - ); - - expect(wrapper.find(`[data-test-subj="attach-alert-to-case-button"]`).exists()).toBeTruthy(); - }); - - it('it opens the context menu', () => { - const wrapper = mount( - - - - - ); - - wrapper.find(`[data-test-subj="attach-alert-to-case-button"]`).first().simulate('click'); - expect(wrapper.find(`[data-test-subj="add-new-case-item"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="add-existing-case-menu-item"]`).exists()).toBeTruthy(); - }); - - it('it opens the create case flyout', () => { - const wrapper = mount( - - - - - ); - - wrapper.find(`[data-test-subj="attach-alert-to-case-button"]`).first().simulate('click'); - wrapper.find(`[data-test-subj="add-new-case-item"]`).first().simulate('click'); - expect(mockGetCreateCaseFlyout).toHaveBeenCalled(); - }); - - it('it opens the all cases modal', () => { - const wrapper = mount( - - - - - ); - - wrapper.find(`[data-test-subj="attach-alert-to-case-button"]`).first().simulate('click'); - wrapper.find(`[data-test-subj="add-existing-case-menu-item"]`).first().simulate('click'); - - expect(mockGetAllCasesSelectorModal).toHaveBeenCalled(); - }); - - it('it set rule information as null when missing', () => { - const wrapper = mount( - - - - - ); - - wrapper.find(`[data-test-subj="attach-alert-to-case-button"]`).first().simulate('click'); - wrapper.find(`[data-test-subj="add-existing-case-menu-item"]`).first().simulate('click'); - expect(mockGetAllCasesSelectorModal.mock.calls[0][0].alertData).toEqual({ - alertId: 'test-id', - index: 'test-index', - rule: { - id: 'rule-id', - name: null, - }, - owner: SECURITY_SOLUTION_OWNER, - }); - }); - - it('disabled when event type is not supported', () => { - const wrapper = mount( - - - - - ); - - expect( - wrapper.find(`[data-test-subj="attach-alert-to-case-button"]`).first().prop('isDisabled') - ).toBeTruthy(); - }); - - it('hides the icon when user does not have crud permissions', () => { - const newProps = { - ...props, - casePermissions: { - crud: false, - read: true, - }, - }; - const wrapper = mount( - - - - - ); - - expect(wrapper.find(`[data-test-subj="attach-alert-to-case-button"]`).exists()).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.tsx deleted file mode 100644 index 193d63e2e849c..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action.tsx +++ /dev/null @@ -1,147 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, useMemo, useCallback } from 'react'; -import { useDispatch } from 'react-redux'; -import { - GetAllCasesSelectorModalProps, - GetCreateCaseFlyoutProps, -} from '../../../../../../cases/public'; -import { - CaseStatuses, - StatusAll, - CasesFeatures, - CommentType, -} from '../../../../../../cases/common'; -import { TimelineItem } from '../../../../../common/search_strategy'; -import { useAddToCase, normalizedEventFields } from '../../../../hooks/use_add_to_case'; -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { TimelinesStartServices } from '../../../../types'; -import { setOpenAddToExistingCase, setOpenAddToNewCase } from '../../../../store/t_grid/actions'; - -export interface AddToCaseActionProps { - event?: TimelineItem; - useInsertTimeline?: Function; - casePermissions: { - crud: boolean; - read: boolean; - } | null; - appId: string; - owner: string; - onClose?: Function; - casesFeatures?: CasesFeatures; -} - -const AddToCaseActionComponent: React.FC = ({ - event, - useInsertTimeline, - casePermissions, - appId, - owner, - onClose, - casesFeatures, -}) => { - const eventId = event?.ecs._id ?? ''; - const eventIndex = event?.ecs._index ?? ''; - const dispatch = useDispatch(); - const { cases } = useKibana().services; - const { - onCaseClicked, - onCaseSuccess, - onCaseCreated, - isAllCaseModalOpen, - isCreateCaseFlyoutOpen, - } = useAddToCase({ event, casePermissions, appId, owner, onClose }); - - const allCasesSelectorModalProps: GetAllCasesSelectorModalProps = useMemo(() => { - const { ruleId, ruleName } = normalizedEventFields(event); - return { - alertData: { - alertId: eventId, - index: eventIndex ?? '', - rule: { - id: ruleId, - name: ruleName, - }, - owner, - }, - hooks: { - useInsertTimeline, - }, - hiddenStatuses: [CaseStatuses.closed, StatusAll], - onRowClick: onCaseClicked, - updateCase: onCaseSuccess, - userCanCrud: casePermissions?.crud ?? false, - owner: [owner], - onClose: () => dispatch(setOpenAddToExistingCase({ id: eventId, isOpen: false })), - }; - }, [ - casePermissions?.crud, - onCaseSuccess, - onCaseClicked, - eventId, - eventIndex, - dispatch, - owner, - useInsertTimeline, - event, - ]); - - const closeCaseFlyoutOpen = useCallback(() => { - dispatch(setOpenAddToNewCase({ id: eventId, isOpen: false })); - }, [dispatch, eventId]); - - const createCaseFlyoutProps: GetCreateCaseFlyoutProps = useMemo(() => { - const { ruleId, ruleName } = normalizedEventFields(event); - const attachments = [ - { - alertId: eventId, - index: eventIndex ?? '', - rule: { - id: ruleId, - name: ruleName, - }, - owner, - type: CommentType.alert as const, - }, - ]; - return { - afterCaseCreated: onCaseCreated, - onClose: closeCaseFlyoutOpen, - onSuccess: onCaseSuccess, - useInsertTimeline, - owner: [owner], - userCanCrud: casePermissions?.crud ?? false, - features: casesFeatures, - attachments, - }; - }, [ - event, - eventId, - eventIndex, - owner, - onCaseCreated, - closeCaseFlyoutOpen, - onCaseSuccess, - useInsertTimeline, - casePermissions?.crud, - casesFeatures, - ]); - - return ( - <> - {isCreateCaseFlyoutOpen && cases.getCreateCaseFlyout(createCaseFlyoutProps)} - {isAllCaseModalOpen && cases.getAllCasesSelectorModal(allCasesSelectorModalProps)} - - ); -}; -AddToCaseActionComponent.displayName = 'AddToCaseAction'; - -export const AddToCaseAction = memo(AddToCaseActionComponent); - -// eslint-disable-next-line import/no-default-export -export default AddToCaseAction; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action_button.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action_button.tsx deleted file mode 100644 index 9757bd94b1746..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_case_action_button.tsx +++ /dev/null @@ -1,109 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, useMemo } from 'react'; -import { - EuiPopover, - EuiButtonIcon, - EuiContextMenuPanel, - EuiText, - EuiContextMenuItem, - EuiToolTip, -} from '@elastic/eui'; -import { AddToCaseActionProps } from './add_to_case_action'; -import { useAddToCase } from '../../../../hooks/use_add_to_case'; -import { ActionIconItem } from '../../action_icon_item'; -import * as i18n from './translations'; - -const AddToCaseActionButtonComponent: React.FC = ({ - event, - useInsertTimeline, - casePermissions, - appId, - owner, - onClose, -}) => { - const { - addNewCaseClick, - addExistingCaseClick, - isDisabled, - userCanCrud, - isEventSupported, - openPopover, - closePopover, - isPopoverOpen, - } = useAddToCase({ event, useInsertTimeline, casePermissions, appId, owner, onClose }); - const tooltipContext = userCanCrud - ? isEventSupported - ? i18n.ACTION_ADD_TO_CASE_TOOLTIP - : i18n.UNSUPPORTED_EVENTS_MSG - : i18n.PERMISSIONS_MSG; - const items = useMemo( - () => [ - - {i18n.ACTION_ADD_NEW_CASE} - , - - {i18n.ACTION_ADD_EXISTING_CASE} - , - ], - [addExistingCaseClick, addNewCaseClick, isDisabled] - ); - - const button = useMemo( - () => ( - - - - ), - [isDisabled, openPopover, tooltipContext] - ); - - return ( - <> - {userCanCrud && ( - - - - - - )} - - ); -}; - -export const AddToCaseActionButton = memo(AddToCaseActionButtonComponent); - -// eslint-disable-next-line import/no-default-export -export default AddToCaseActionButton; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx deleted file mode 100644 index 4d19a89096882..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx +++ /dev/null @@ -1,76 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo } from 'react'; -import { EuiContextMenuItem } from '@elastic/eui'; - -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { TimelinesStartServices } from '../../../../types'; -import { useAddToCase } from '../../../../hooks/use_add_to_case'; -import { AddToCaseActionProps } from './add_to_case_action'; -import * as i18n from './translations'; - -interface AddToCaseActionButtonProps extends AddToCaseActionProps { - ariaLabel?: string; -} - -const AddToCaseActionButtonComponent: React.FC = ({ - ariaLabel = i18n.ACTION_ADD_TO_CASE_ARIA_LABEL, - event, - useInsertTimeline, - casePermissions, - appId, - owner, - onClose, -}) => { - const { onCaseSuccess, onCaseClicked, isDisabled, userCanCrud, caseAttachments } = useAddToCase({ - event, - useInsertTimeline, - casePermissions, - appId, - owner, - onClose, - }); - const { cases } = useKibana().services; - const addToCaseModal = cases.hooks.getUseCasesAddToExistingCaseModal({ - attachments: caseAttachments, - updateCase: onCaseSuccess, - onRowClick: onCaseClicked, - }); - - // TODO To be further refactored and moved to cases plugins - // https://github.com/elastic/kibana/issues/123183 - const handleClick = () => { - // close the popover - if (onClose) { - onClose(); - } - addToCaseModal.open(); - }; - - return ( - <> - {userCanCrud && ( - - {i18n.ACTION_ADD_EXISTING_CASE} - - )} - - ); -}; - -export const AddToExistingCaseButton = memo(AddToCaseActionButtonComponent); - -// eslint-disable-next-line import/no-default-export -export default AddToExistingCaseButton; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_new_case_button.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_new_case_button.tsx deleted file mode 100644 index eb83cb21aea6e..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_new_case_button.tsx +++ /dev/null @@ -1,77 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo } from 'react'; -import { EuiContextMenuItem } from '@elastic/eui'; - -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { TimelinesStartServices } from '../../../../types'; -import { useAddToCase } from '../../../../hooks/use_add_to_case'; -import { AddToCaseActionProps } from './add_to_case_action'; -import * as i18n from './translations'; - -export interface AddToNewCaseButtonProps extends AddToCaseActionProps { - ariaLabel?: string; -} - -const AddToNewCaseButtonComponent: React.FC = ({ - ariaLabel = i18n.ACTION_ADD_TO_CASE_ARIA_LABEL, - event, - useInsertTimeline, - casePermissions, - appId, - owner, - onClose, -}) => { - const { isDisabled, userCanCrud, caseAttachments, onCaseSuccess, onCaseCreated } = useAddToCase({ - event, - useInsertTimeline, - casePermissions, - appId, - owner, - onClose, - }); - const { cases } = useKibana().services; - const createCaseFlyout = cases.hooks.getUseCasesAddToNewCaseFlyout({ - attachments: caseAttachments, - afterCaseCreated: onCaseCreated, - onSuccess: onCaseSuccess, - }); - - // TODO To be further refactored and moved to cases plugins - // https://github.com/elastic/kibana/issues/123183 - const handleClick = () => { - // close the popover - if (onClose) { - onClose(); - } - createCaseFlyout.open(); - }; - - return ( - <> - {userCanCrud && ( - - {i18n.ACTION_ADD_NEW_CASE} - - )} - - ); -}; -AddToNewCaseButtonComponent.displayName = 'AddToNewCaseButton'; - -export const AddToNewCaseButton = memo(AddToNewCaseButtonComponent); - -// eslint-disable-next-line import/no-default-export -export default AddToNewCaseButton; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/helpers.test.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/helpers.test.tsx deleted file mode 100644 index efb11393a4244..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/helpers.test.tsx +++ /dev/null @@ -1,45 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import 'jest-styled-components'; -import type { MockedKeys } from '@kbn/utility-types/jest'; -import { CoreStart } from 'kibana/public'; -import { coreMock } from 'src/core/public/mocks'; -import type { IToasts } from '../../../../../../../../src/core/public'; - -import { createUpdateSuccessToaster } from './helpers'; -import { Case } from '../../../../../../cases/common'; - -let mockCoreStart: MockedKeys; -let toasts: IToasts; -let toastsSpy: jest.SpyInstance; - -const theCase = { - id: 'case-id', - title: 'My case', - settings: { - syncAlerts: true, - }, -} as Case; - -describe('helpers', () => { - beforeEach(() => { - mockCoreStart = coreMock.createStart(); - }); - - describe('createUpdateSuccessToaster', () => { - it('creates the correct toast when the sync alerts is on', () => { - const onViewCaseClick = jest.fn(); - - toasts = mockCoreStart.notifications.toasts; - toastsSpy = jest.spyOn(mockCoreStart.notifications.toasts, 'addSuccess'); - createUpdateSuccessToaster(toasts, theCase, onViewCaseClick); - - expect(toastsSpy).toHaveBeenCalled(); - }); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/helpers.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/helpers.tsx deleted file mode 100644 index 71e2f41a5288a..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/helpers.tsx +++ /dev/null @@ -1,43 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import styled from 'styled-components'; -import { ToasterContent } from './toaster_content'; -import * as i18n from './translations'; -import type { Case } from '../../../../../../cases/common'; -import type { ToastsStart, Toast } from '../../../../../../../../src/core/public'; -import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; - -const LINE_CLAMP = 3; - -const Title = styled.span` - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: ${LINE_CLAMP}; - -webkit-box-orient: vertical; - overflow: hidden; -`; - -export const createUpdateSuccessToaster = ( - toasts: ToastsStart, - theCase: Case, - onViewCaseClick: (id: string) => void -): Toast => { - return toasts.addSuccess({ - color: 'success', - iconType: 'check', - title: toMountPoint({i18n.CASE_CREATED_SUCCESS_TOAST(theCase.title)}), - text: toMountPoint( - - ), - }); -}; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/index.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/index.tsx deleted file mode 100644 index bb3bd63e316ed..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/index.tsx +++ /dev/null @@ -1,11 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './add_to_case_action'; -export * from './toaster_content'; -export * from './add_to_existing_case_button'; -export * from './add_to_new_case_button'; diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/toaster_content.test.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/toaster_content.test.tsx deleted file mode 100644 index fd20366e7891f..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/toaster_content.test.tsx +++ /dev/null @@ -1,46 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount } from 'enzyme'; - -import { ToasterContent } from './toaster_content'; - -describe('ToasterContent', () => { - const onViewCaseClick = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('renders with syncAlerts=true', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="toaster-content-case-view-link"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="toaster-content-sync-text"]').exists()).toBeTruthy(); - }); - - it('renders with syncAlerts=false', () => { - const wrapper = mount( - - ); - - expect(wrapper.find('[data-test-subj="toaster-content-case-view-link"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="toaster-content-sync-text"]').exists()).toBeFalsy(); - }); - - it('calls onViewCaseClick', () => { - const wrapper = mount( - - ); - - wrapper.find('[data-test-subj="toaster-content-case-view-link"]').first().simulate('click'); - expect(onViewCaseClick).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/toaster_content.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/toaster_content.tsx deleted file mode 100644 index 147dd3f5e8399..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/toaster_content.tsx +++ /dev/null @@ -1,47 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, useCallback } from 'react'; -import { EuiButtonEmpty, EuiText } from '@elastic/eui'; -import styled from 'styled-components'; - -import * as i18n from './translations'; - -const EuiTextStyled = styled(EuiText)` - ${({ theme }) => ` - margin-bottom: ${theme.eui?.paddingSizes?.s ?? 8}px; - `} -`; - -interface Props { - caseId: string; - syncAlerts: boolean; - onViewCaseClick: (id: string) => void; -} - -const ToasterContentComponent: React.FC = ({ caseId, syncAlerts, onViewCaseClick }) => { - const onClick = useCallback(() => onViewCaseClick(caseId), [caseId, onViewCaseClick]); - return ( - <> - {syncAlerts && ( - - {i18n.CASE_CREATED_SUCCESS_TOAST_TEXT} - - )} - - {i18n.VIEW_CASE} - - - ); -}; - -export const ToasterContent = memo(ToasterContentComponent); diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/translations.ts b/x-pack/plugins/timelines/public/components/actions/timeline/cases/translations.ts deleted file mode 100644 index df0dfb9048ace..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/translations.ts +++ /dev/null @@ -1,75 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const ACTION_ADD_CASE = i18n.translate('xpack.timelines.cases.timeline.actions.addCase', { - defaultMessage: 'Add to case', -}); - -export const ACTION_ADD_NEW_CASE = i18n.translate( - 'xpack.timelines.cases.timeline.actions.addNewCase', - { - defaultMessage: 'Add to new case', - } -); - -export const ACTION_ADD_EXISTING_CASE = i18n.translate( - 'xpack.timelines.cases.timeline.actions.addExistingCase', - { - defaultMessage: 'Add to existing case', - } -); - -export const ACTION_ADD_TO_CASE_ARIA_LABEL = i18n.translate( - 'xpack.timelines.cases.timeline.actions.addToCaseAriaLabel', - { - defaultMessage: 'Attach alert to case', - } -); - -export const ACTION_ADD_TO_CASE_TOOLTIP = i18n.translate( - 'xpack.timelines.cases.timeline.actions.addToCaseTooltip', - { - defaultMessage: 'Add to case', - } -); - -export const CASE_CREATED_SUCCESS_TOAST = (title: string) => - i18n.translate('xpack.timelines.cases.timeline.actions.caseCreatedSuccessToast', { - values: { title }, - defaultMessage: 'An alert has been added to "{title}"', - }); - -export const CASE_CREATED_SUCCESS_TOAST_TEXT = i18n.translate( - 'xpack.timelines.cases.timeline.actions.caseCreatedSuccessToastText', - { - defaultMessage: 'Alerts in this case have their status synched with the case status', - } -); - -export const VIEW_CASE = i18n.translate( - 'xpack.timelines.cases.timeline.actions.caseCreatedSuccessToastViewCaseLink', - { - defaultMessage: 'View Case', - } -); - -export const PERMISSIONS_MSG = i18n.translate( - 'xpack.timelines.cases.timeline.actions.permissionsMessage', - { - defaultMessage: - 'You are currently missing the required permissions to attach alerts to cases. Please contact your administrator for further assistance.', - } -); - -export const UNSUPPORTED_EVENTS_MSG = i18n.translate( - 'xpack.timelines.cases.timeline.actions.unsupportedEventsMessage', - { - defaultMessage: 'This event cannot be attached to a case', - } -); diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/index.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/index.tsx deleted file mode 100644 index c5a69fd5e5745..0000000000000 --- a/x-pack/plugins/timelines/public/components/actions/timeline/index.tsx +++ /dev/null @@ -1,8 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './cases'; diff --git a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx index f0488d26f5f16..be71d159eafca 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx @@ -8,7 +8,7 @@ import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; import React, { useEffect, useMemo, useState, useRef } from 'react'; import styled from 'styled-components'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Filter, Query } from '@kbn/es-query'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; @@ -39,7 +39,6 @@ import { LastUpdatedAt } from '../..'; import { SELECTOR_TIMELINE_GLOBAL_CONTAINER, UpdatedFlexItem, UpdatedFlexGroup } from '../styles'; import { InspectButton, InspectButtonContainer } from '../../inspect'; import { useFetchIndex } from '../../../container/source'; -import { AddToCaseAction } from '../../actions/timeline/cases/add_to_case_action'; import { TGridLoading, TGridEmpty, TimelineContext } from '../shared'; const FullWidthFlexGroup = styled(EuiFlexGroup)<{ $visible: boolean }>` @@ -76,16 +75,7 @@ const ScrollableFlexItem = styled(EuiFlexItem)` overflow: auto; `; -const casesFeatures = { alerts: { sync: false } }; - export interface TGridStandaloneProps { - appId: string; - casesOwner: string; - casePermissions: { - crud: boolean; - read: boolean; - } | null; - afterCaseSelection?: Function; columns: ColumnHeaderOptions[]; dataViewId?: string | null; defaultCellActions?: TGridCellAction[]; @@ -127,10 +117,6 @@ export interface TGridStandaloneProps { } const TGridStandaloneComponent: React.FC = ({ - afterCaseSelection, - appId, - casesOwner, - casePermissions, columns, dataViewId = null, defaultCellActions, @@ -272,26 +258,6 @@ const TGridStandaloneComponent: React.FC = ({ ); const hasAlerts = totalCountMinusDeleted > 0; - const activeCaseFlowId = useSelector((state: State) => tGridSelectors.activeCaseFlowId(state)); - const selectedEvent = useMemo(() => { - const matchedEvent = events.find((event) => event.ecs._id === activeCaseFlowId); - if (matchedEvent) { - return matchedEvent; - } else { - return undefined; - } - }, [events, activeCaseFlowId]); - - const addToCaseActionProps = useMemo(() => { - return { - event: selectedEvent, - casePermissions: casePermissions ?? null, - appId, - owner: casesOwner, - onClose: afterCaseSelection, - }; - }, [appId, casePermissions, afterCaseSelection, selectedEvent, casesOwner]); - const nonDeletedEvents = useMemo( () => events.filter((e) => !deletedEventIds.includes(e._id)), [deletedEventIds, events] @@ -425,7 +391,6 @@ const TGridStandaloneComponent: React.FC = ({ ) : null} - ); diff --git a/x-pack/plugins/timelines/public/hooks/use_add_to_case.test.ts b/x-pack/plugins/timelines/public/hooks/use_add_to_case.test.ts deleted file mode 100644 index 5b654f40deea6..0000000000000 --- a/x-pack/plugins/timelines/public/hooks/use_add_to_case.test.ts +++ /dev/null @@ -1,71 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { normalizedEventFields } from './use_add_to_case'; -import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; -import { merge } from 'lodash'; - -const defaultArgs = { - _id: 'test-id', - data: [ - { field: '@timestamp', value: ['2018-11-05T19:03:25.937Z'] }, - { field: ALERT_RULE_UUID, value: ['data-rule-id'] }, - { field: ALERT_RULE_NAME, value: ['data-rule-name'] }, - ], - ecs: { - _id: 'test-id', - _index: 'test-index', - signal: { rule: { id: ['rule-id'], name: ['rule-name'], false_positives: [] } }, - }, -}; -describe('normalizedEventFields', () => { - it('uses rule data when provided', () => { - const result = normalizedEventFields(defaultArgs); - expect(result).toEqual({ - ruleId: 'data-rule-id', - ruleName: 'data-rule-name', - }); - }); - const makeObj = (s: string, v: string[]) => { - const keys = s.split('.'); - return keys - .reverse() - .reduce((prev, current, i) => (i === 0 ? { [current]: v } : { [current]: { ...prev } }), {}); - }; - it('uses rule/ecs combo Xavier thinks is a thing but Steph has yet to see', () => { - const args = { - ...defaultArgs, - data: [], - ecs: { - _id: 'string', - ...merge( - makeObj(ALERT_RULE_UUID, ['xavier-rule-id']), - makeObj(ALERT_RULE_NAME, ['xavier-rule-name']) - ), - }, - }; - const result = normalizedEventFields(args); - expect(result).toEqual({ - ruleId: 'xavier-rule-id', - ruleName: 'xavier-rule-name', - }); - }); - it('falls back to use ecs data', () => { - const result = normalizedEventFields({ ...defaultArgs, data: [] }); - expect(result).toEqual({ - ruleId: 'rule-id', - ruleName: 'rule-name', - }); - }); - it('returns null when all the data is bad', () => { - const result = normalizedEventFields({ ...defaultArgs, data: [], ecs: { _id: 'bad' } }); - expect(result).toEqual({ - ruleId: null, - ruleName: null, - }); - }); -}); diff --git a/x-pack/plugins/timelines/public/hooks/use_add_to_case.ts b/x-pack/plugins/timelines/public/hooks/use_add_to_case.ts deleted file mode 100644 index 63a7d07831c4b..0000000000000 --- a/x-pack/plugins/timelines/public/hooks/use_add_to_case.ts +++ /dev/null @@ -1,200 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { get, isEmpty } from 'lodash/fp'; -import { useState, useCallback, useMemo, SyntheticEvent } from 'react'; -import { useDispatch } from 'react-redux'; -import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; -import { useKibana } from '../../../../../src/plugins/kibana_react/public'; -import { Case, CommentType } from '../../../cases/common'; -import { TimelinesStartServices } from '../types'; -import { TimelineItem } from '../../common/search_strategy'; -import { tGridActions } from '../store/t_grid'; -import { useDeepEqualSelector } from './use_selector'; -import { createUpdateSuccessToaster } from '../components/actions/timeline/cases/helpers'; -import { AddToCaseActionProps } from '../components/actions'; -import { CaseAttachments, CasesDeepLinkId, generateCaseViewPath } from '../../../cases/public'; - -interface UseAddToCase { - addNewCaseClick: () => void; - addExistingCaseClick: () => void; - onCaseClicked: (theCase?: Case) => void; - onCaseSuccess: (theCase: Case) => Promise; - onCaseCreated: () => Promise; - isAllCaseModalOpen: boolean; - isDisabled: boolean; - userCanCrud: boolean; - isEventSupported: boolean; - openPopover: (event: SyntheticEvent) => void; - closePopover: () => void; - isPopoverOpen: boolean; - isCreateCaseFlyoutOpen: boolean; - caseAttachments?: CaseAttachments; -} - -export const useAddToCase = ({ - event, - casePermissions, - appId, - onClose, - owner, -}: AddToCaseActionProps): UseAddToCase => { - const eventId = event?.ecs._id ?? ''; - const dispatch = useDispatch(); - // TODO: use correct value in standalone or integrated. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const timelineById = useDeepEqualSelector((state: any) => { - if (state.timeline) { - return state.timeline.timelineById[eventId]; - } else { - return state.timelineById[eventId]; - } - }); - const isAllCaseModalOpen = useMemo(() => { - if (timelineById) { - return timelineById.isAddToExistingCaseOpen; - } else { - return false; - } - }, [timelineById]); - const isCreateCaseFlyoutOpen = useMemo(() => { - if (timelineById) { - return timelineById.isCreateNewCaseOpen; - } else { - return false; - } - }, [timelineById]); - const { - application: { navigateToApp }, - notifications: { toasts }, - } = useKibana().services; - - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const openPopover = useCallback(() => setIsPopoverOpen(true), []); - const closePopover = useCallback(() => setIsPopoverOpen(false), []); - - const isEventSupported = useMemo(() => { - if (event !== undefined) { - if (event.data.some(({ field }) => field === 'kibana.alert.rule.uuid')) { - return true; - } - return !isEmpty(event.ecs.signal?.rule?.id ?? event.ecs.kibana?.alert?.rule?.uuid); - } else { - return false; - } - }, [event]); - - const userCanCrud = casePermissions?.crud ?? false; - const isDisabled = !userCanCrud || !isEventSupported; - - const onViewCaseClick = useCallback( - (id) => { - navigateToApp(appId, { - deepLinkId: CasesDeepLinkId.cases, - path: generateCaseViewPath({ detailName: id }), - }); - }, - [navigateToApp, appId] - ); - - const onCaseCreated = useCallback(async () => { - dispatch(tGridActions.setOpenAddToNewCase({ id: eventId, isOpen: false })); - }, [eventId, dispatch]); - - const onCaseSuccess = useCallback( - async (theCase: Case) => { - dispatch(tGridActions.setOpenAddToExistingCase({ id: eventId, isOpen: false })); - createUpdateSuccessToaster(toasts, theCase, onViewCaseClick); - }, - [onViewCaseClick, toasts, dispatch, eventId] - ); - const caseAttachments: CaseAttachments = useMemo(() => { - const eventIndex = event?.ecs._index ?? ''; - const { ruleId, ruleName } = normalizedEventFields(event); - const attachments = [ - { - alertId: eventId, - index: eventIndex ?? '', - rule: { - id: ruleId, - name: ruleName, - }, - owner, - type: CommentType.alert as const, - }, - ]; - return attachments; - }, [event, eventId, owner]); - - const onCaseClicked = useCallback( - (theCase?: Case) => { - /** - * No cases listed on the table. - * The user pressed the add new case table's button. - * We gonna open the create case modal. - */ - if (theCase == null) { - dispatch(tGridActions.setOpenAddToNewCase({ id: eventId, isOpen: true })); - } - dispatch(tGridActions.setOpenAddToExistingCase({ id: eventId, isOpen: false })); - }, - [dispatch, eventId] - ); - const addNewCaseClick = useCallback(() => { - closePopover(); - dispatch(tGridActions.setOpenAddToNewCase({ id: eventId, isOpen: true })); - if (onClose) { - onClose(); - } - }, [onClose, closePopover, dispatch, eventId]); - - const addExistingCaseClick = useCallback(() => { - closePopover(); - dispatch(tGridActions.setOpenAddToExistingCase({ id: eventId, isOpen: true })); - if (onClose) { - onClose(); - } - }, [onClose, closePopover, dispatch, eventId]); - return { - caseAttachments, - addNewCaseClick, - addExistingCaseClick, - onCaseClicked, - onCaseSuccess, - onCaseCreated, - isAllCaseModalOpen, - isDisabled, - userCanCrud, - isEventSupported, - openPopover, - closePopover, - isPopoverOpen, - isCreateCaseFlyoutOpen, - }; -}; - -export function normalizedEventFields(event?: TimelineItem) { - const ruleUuidData = event && event.data.find(({ field }) => field === ALERT_RULE_UUID); - const ruleNameData = event && event.data.find(({ field }) => field === ALERT_RULE_NAME); - const ruleUuidValueData = ruleUuidData && ruleUuidData.value && ruleUuidData.value[0]; - const ruleNameValueData = ruleNameData && ruleNameData.value && ruleNameData.value[0]; - - const ruleUuid = - ruleUuidValueData ?? - get(`ecs.${ALERT_RULE_UUID}[0]`, event) ?? - get(`ecs.signal.rule.id[0]`, event) ?? - null; - const ruleName = - ruleNameValueData ?? - get(`ecs.${ALERT_RULE_NAME}[0]`, event) ?? - get(`ecs.signal.rule.name[0]`, event) ?? - null; - - return { - ruleId: ruleUuid, - ruleName, - }; -} diff --git a/x-pack/plugins/timelines/public/methods/index.tsx b/x-pack/plugins/timelines/public/methods/index.tsx index c8b5e28c2d21e..7d9ec4bcaab27 100644 --- a/x-pack/plugins/timelines/public/methods/index.tsx +++ b/x-pack/plugins/timelines/public/methods/index.tsx @@ -7,14 +7,11 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { I18nProvider } from '@kbn/i18n-react'; import type { Store } from 'redux'; -import { Provider } from 'react-redux'; import type { Storage } from '../../../../../src/plugins/kibana_utils/public'; import type { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import type { TGridProps } from '../types'; import type { LastUpdatedAtProps, LoadingPanelProps, FieldBrowserProps } from '../components'; -import type { AddToCaseActionProps } from '../components/actions/timeline/cases/add_to_case_action'; import { initialTGridState } from '../store/t_grid/reducer'; import { createStore } from '../store/t_grid'; import { TGridLoading } from '../components/t_grid/shared'; @@ -84,76 +81,3 @@ export const getFieldsBrowserLazy = (props: FieldBrowserProps, { store }: { stor ); }; - -const AddToCaseLazy = lazy(() => import('../components/actions/timeline/cases/add_to_case_action')); -export const getAddToCaseLazy = ( - props: AddToCaseActionProps, - { store, storage, setStore }: { store: Store; storage: Storage; setStore: (store: Store) => void } -) => { - return ( - }> - - - - - - - ); -}; - -const AddToCasePopover = lazy( - () => import('../components/actions/timeline/cases/add_to_case_action_button') -); -export const getAddToCasePopoverLazy = ( - props: AddToCaseActionProps, - { store, storage, setStore }: { store: Store; storage: Storage; setStore: (store: Store) => void } -) => { - initializeStore({ store, storage, setStore }); - return ( - }> - - - - - - - ); -}; - -const AddToExistingButton = lazy( - () => import('../components/actions/timeline/cases/add_to_existing_case_button') -); -export const getAddToExistingCaseButtonLazy = ( - props: AddToCaseActionProps, - { store, storage, setStore }: { store: Store; storage: Storage; setStore: (store: Store) => void } -) => { - initializeStore({ store, storage, setStore }); - return ( - }> - - - - - - - ); -}; - -const AddToNewCaseButton = lazy( - () => import('../components/actions/timeline/cases/add_to_new_case_button') -); -export const getAddToNewCaseButtonLazy = ( - props: AddToCaseActionProps, - { store, storage, setStore }: { store: Store; storage: Storage; setStore: (store: Store) => void } -) => { - initializeStore({ store, storage, setStore }); - return ( - }> - - - - - - - ); -}; diff --git a/x-pack/plugins/timelines/public/mock/global_state.ts b/x-pack/plugins/timelines/public/mock/global_state.ts index fea8aa57b88dd..955a612f89c1d 100644 --- a/x-pack/plugins/timelines/public/mock/global_state.ts +++ b/x-pack/plugins/timelines/public/mock/global_state.ts @@ -34,8 +34,6 @@ export const mockGlobalState: TimelineState = { 'packetbeat-*', 'winlogbeat-*', ], - isAddToExistingCaseOpen: false, - isCreateNewCaseOpen: false, isLoading: false, isSelectAllChecked: false, itemsPerPage: 5, diff --git a/x-pack/plugins/timelines/public/mock/mock_timeline_data.ts b/x-pack/plugins/timelines/public/mock/mock_timeline_data.ts index 363a67a30b978..55ec6862309aa 100644 --- a/x-pack/plugins/timelines/public/mock/mock_timeline_data.ts +++ b/x-pack/plugins/timelines/public/mock/mock_timeline_data.ts @@ -1566,8 +1566,6 @@ export const mockTgridModel: TGridModel = { selectAll: false, id: 'ef579e40-jibber-jabber', indexNames: [], - isAddToExistingCaseOpen: false, - isCreateNewCaseOpen: false, isLoading: false, isSelectAllChecked: false, kqlQuery: { diff --git a/x-pack/plugins/timelines/public/mock/plugin_mock.tsx b/x-pack/plugins/timelines/public/mock/plugin_mock.tsx index 066485319f911..be2adfe84a101 100644 --- a/x-pack/plugins/timelines/public/mock/plugin_mock.tsx +++ b/x-pack/plugins/timelines/public/mock/plugin_mock.tsx @@ -24,6 +24,4 @@ export const createTGridMocks = () => ({ getUseAddToTimeline: () => useAddToTimeline, getUseAddToTimelineSensor: () => useAddToTimelineSensor, getUseDraggableKeyboardWrapper: () => useDraggableKeyboardWrapper, - getAddToExistingCaseButton: () =>
, - getAddToNewCaseButton: () =>
, }); diff --git a/x-pack/plugins/timelines/public/plugin.ts b/x-pack/plugins/timelines/public/plugin.ts index 0ecb063445a46..8b4bdae43dfe3 100644 --- a/x-pack/plugins/timelines/public/plugin.ts +++ b/x-pack/plugins/timelines/public/plugin.ts @@ -16,10 +16,6 @@ import { getLoadingPanelLazy, getTGridLazy, getFieldsBrowserLazy, - getAddToCaseLazy, - getAddToExistingCaseButtonLazy, - getAddToNewCaseButtonLazy, - getAddToCasePopoverLazy, } from './methods'; import type { TimelinesUIStart, TGridProps, TimelinesStartPlugins } from './types'; import { tGridReducer } from './store/t_grid/reducer'; @@ -88,38 +84,6 @@ export class TimelinesPlugin implements Plugin { setTGridEmbeddedStore: (store: Store) => { this.setStore(store); }, - getAddToCaseAction: (props) => { - return getAddToCaseLazy(props, { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - store: this._store!, - storage: this._storage, - setStore: this.setStore.bind(this), - }); - }, - getAddToCasePopover: (props) => { - return getAddToCasePopoverLazy(props, { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - store: this._store!, - storage: this._storage, - setStore: this.setStore.bind(this), - }); - }, - getAddToExistingCaseButton: (props) => { - return getAddToExistingCaseButtonLazy(props, { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - store: this._store!, - storage: this._storage, - setStore: this.setStore.bind(this), - }); - }, - getAddToNewCaseButton: (props) => { - return getAddToNewCaseButtonLazy(props, { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - store: this._store!, - storage: this._storage, - setStore: this.setStore.bind(this), - }); - }, }; } diff --git a/x-pack/plugins/timelines/public/store/t_grid/actions.ts b/x-pack/plugins/timelines/public/store/t_grid/actions.ts index feab12b616c78..00e207180b134 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/actions.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/actions.ts @@ -117,11 +117,3 @@ export const setTimelineUpdatedAt = export const addProviderToTimeline = actionCreator<{ id: string; dataProvider: DataProvider }>( 'ADD_PROVIDER_TO_TIMELINE' ); - -export const setOpenAddToExistingCase = actionCreator<{ id: string; isOpen: boolean }>( - 'SET_OPEN_ADD_TO_EXISTING_CASE' -); - -export const setOpenAddToNewCase = actionCreator<{ id: string; isOpen: boolean }>( - 'SET_OPEN_ADD_TO_NEW_CASE' -); diff --git a/x-pack/plugins/timelines/public/store/t_grid/model.ts b/x-pack/plugins/timelines/public/store/t_grid/model.ts index 0640cfb845d9c..82a4c3e68fd02 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/model.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/model.ts @@ -66,8 +66,6 @@ export interface TGridModel extends TGridModelSettings { /** Uniquely identifies the timeline */ id: string; indexNames: string[]; - isAddToExistingCaseOpen: boolean; - isCreateNewCaseOpen: boolean; isLoading: boolean; /** If selectAll checkbox in header is checked **/ isSelectAllChecked: boolean; diff --git a/x-pack/plugins/timelines/public/store/t_grid/reducer.ts b/x-pack/plugins/timelines/public/store/t_grid/reducer.ts index d3af1dc4e9b30..ae3f9dc5c20b3 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/reducer.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/reducer.ts @@ -18,8 +18,6 @@ import { setEventsDeleted, setEventsLoading, setTGridSelectAll, - setOpenAddToExistingCase, - setOpenAddToNewCase, setSelected, setTimelineUpdatedAt, toggleDetailPanel, @@ -239,26 +237,6 @@ export const tGridReducer = reducerWithInitialState(initialTGridState) ...state, timelineById: addProviderToTimelineHelper(id, dataProvider, state.timelineById), })) - .case(setOpenAddToExistingCase, (state, { id, isOpen }) => ({ - ...state, - timelineById: { - ...state.timelineById, - [id]: { - ...state.timelineById[id], - isAddToExistingCaseOpen: isOpen, - }, - }, - })) - .case(setOpenAddToNewCase, (state, { id, isOpen }) => ({ - ...state, - timelineById: { - ...state.timelineById, - [id]: { - ...state.timelineById[id], - isCreateNewCaseOpen: isOpen, - }, - }, - })) .case(setTimelineUpdatedAt, (state, { id, updated }) => ({ ...state, timelineById: { diff --git a/x-pack/plugins/timelines/public/store/t_grid/selectors.ts b/x-pack/plugins/timelines/public/store/t_grid/selectors.ts index 9077cac2b5dd0..2db463e47cc88 100644 --- a/x-pack/plugins/timelines/public/store/t_grid/selectors.ts +++ b/x-pack/plugins/timelines/public/store/t_grid/selectors.ts @@ -6,26 +6,11 @@ */ import { getOr } from 'lodash/fp'; import { createSelector } from 'reselect'; -import { TGridModel, State } from '.'; +import { TGridModel } from '.'; import { tGridDefaults, getTGridManageDefaults } from './defaults'; -interface TGridById { - [id: string]: TGridModel; -} - const getDefaultTgrid = (id: string) => ({ ...tGridDefaults, ...getTGridManageDefaults(id) }); -const standaloneTGridById = (state: State): TGridById => state.timelineById; - -export const activeCaseFlowId = createSelector(standaloneTGridById, (tGrid) => { - return ( - tGrid && - Object.entries(tGrid) - .map(([id, data]) => (data.isAddToExistingCaseOpen || data.isCreateNewCaseOpen ? id : null)) - .find((id) => id) - ); -}); - export const selectTGridById = (state: unknown, timelineId: string): TGridModel => { return getOr( getOr(getDefaultTgrid(timelineId), ['timelineById', timelineId], state), diff --git a/x-pack/plugins/timelines/public/types.ts b/x-pack/plugins/timelines/public/types.ts index ddecac02be705..0c9e6aa3e325f 100644 --- a/x-pack/plugins/timelines/public/types.ts +++ b/x-pack/plugins/timelines/public/types.ts @@ -23,7 +23,6 @@ import type { TGridIntegratedProps } from './components/t_grid/integrated'; import type { TGridStandaloneProps } from './components/t_grid/standalone'; import type { UseAddToTimelineProps, UseAddToTimeline } from './hooks/use_add_to_timeline'; import { HoverActionsConfig } from './components/hover_actions/index'; -import type { AddToCaseActionProps } from './components/actions/timeline/cases/add_to_case_action'; import { TimelineTabs } from '../common/types'; export * from './store/t_grid'; export interface TimelinesUIStart { @@ -42,10 +41,6 @@ export interface TimelinesUIStart { props: UseDraggableKeyboardWrapperProps ) => UseDraggableKeyboardWrapper; setTGridEmbeddedStore: (store: Store) => void; - getAddToCaseAction: (props: AddToCaseActionProps) => ReactElement; - getAddToCasePopover: (props: AddToCaseActionProps) => ReactElement; - getAddToExistingCaseButton: (props: AddToCaseActionProps) => ReactElement; - getAddToNewCaseButton: (props: AddToCaseActionProps) => ReactElement; } export interface TimelinesStartPlugins { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8a39c36c870d4..a80c43bd832d8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -27003,16 +27003,6 @@ "xpack.timelines.alerts.summaryView.options.summaryView.description": "各アラートのイベントフローのレンダリングを表示", "xpack.timelines.beatFields.errorSearchDescription": "Beatフィールドの取得でエラーが発生しました", "xpack.timelines.beatFields.failSearchDescription": "Beat フィールドで検索を実行できませんでした", - "xpack.timelines.cases.timeline.actions.addCase": "ケースに追加", - "xpack.timelines.cases.timeline.actions.addExistingCase": "既存のケースに追加", - "xpack.timelines.cases.timeline.actions.addNewCase": "新しいケースに追加", - "xpack.timelines.cases.timeline.actions.addToCaseAriaLabel": "アラートをケースに関連付ける", - "xpack.timelines.cases.timeline.actions.addToCaseTooltip": "ケースに追加", - "xpack.timelines.cases.timeline.actions.caseCreatedSuccessToast": "アラートが「{title}」に追加されました", - "xpack.timelines.cases.timeline.actions.caseCreatedSuccessToastText": "このケースのアラートはステータスがケースステータスと同期されました", - "xpack.timelines.cases.timeline.actions.caseCreatedSuccessToastViewCaseLink": "ケースの表示", - "xpack.timelines.cases.timeline.actions.permissionsMessage": "現在、アラートをケースに関連付けるための必要な権限がありません。サポートについては、管理者にお問い合わせください。", - "xpack.timelines.cases.timeline.actions.unsupportedEventsMessage": "このイベントはケースに関連付けられません", "xpack.timelines.clipboard.copied": "コピー完了", "xpack.timelines.clipboard.copy": "コピー", "xpack.timelines.clipboard.copy.successToastTitle": "フィールド{field}をクリップボードにコピーしました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index bdba6f9225cc0..15a194346a9e1 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -27035,16 +27035,6 @@ "xpack.timelines.alerts.summaryView.options.summaryView.description": "查看每个告警的事件渲染", "xpack.timelines.beatFields.errorSearchDescription": "获取 Beat 字段时发生错误", "xpack.timelines.beatFields.failSearchDescription": "无法对 Beat 字段执行搜索", - "xpack.timelines.cases.timeline.actions.addCase": "添加到案例", - "xpack.timelines.cases.timeline.actions.addExistingCase": "添加到现有案例", - "xpack.timelines.cases.timeline.actions.addNewCase": "添加到新案例", - "xpack.timelines.cases.timeline.actions.addToCaseAriaLabel": "将告警附加到案例", - "xpack.timelines.cases.timeline.actions.addToCaseTooltip": "添加到案例", - "xpack.timelines.cases.timeline.actions.caseCreatedSuccessToast": "告警已添加到“{title}”", - "xpack.timelines.cases.timeline.actions.caseCreatedSuccessToastText": "此案例中的告警的状态已经与案例状态同步", - "xpack.timelines.cases.timeline.actions.caseCreatedSuccessToastViewCaseLink": "查看案例", - "xpack.timelines.cases.timeline.actions.permissionsMessage": "您当前缺少所需的权限,无法向案例附加告警。有关进一步帮助,请联系您的管理员。", - "xpack.timelines.cases.timeline.actions.unsupportedEventsMessage": "此事件无法附加到案例", "xpack.timelines.clipboard.copied": "已复制", "xpack.timelines.clipboard.copy": "复制", "xpack.timelines.clipboard.copy.successToastTitle": "已将字段 {field} 复制到剪贴板", diff --git a/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx b/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx index c034ba1612a2c..2542e82c5e28d 100644 --- a/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx +++ b/x-pack/test/plugin_functional/plugins/timelines_test/public/applications/timelines_test/index.tsx @@ -65,13 +65,7 @@ const AppRoot = React.memo( {(timelinesPluginSetup && timelinesPluginSetup.getTGrid && timelinesPluginSetup.getTGrid<'standalone'>({ - appId: 'securitySolution', - casesOwner: 'securitySolutionUI', type: 'standalone', - casePermissions: { - read: true, - crud: true, - }, columns: [], indexNames: [], deletedEventIds: [], From 720fbed5215747cc7b8f4ade33b5735f420a693f Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 8 Mar 2022 11:09:03 +0200 Subject: [PATCH 050/140] [Gauge] Vis editors gauge legacy percent mode. (#126318) * Added legacy percentage mode to the gauge. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__snapshots__/gauge_function.test.ts.snap | 14 ++ .../expression_functions/gauge_function.ts | 8 + .../common/types/expression_functions.ts | 2 + .../common/types/expression_renderers.ts | 3 +- .../gauge_component.test.tsx.snap | 1 + .../components/gauge_component.test.tsx | 4 +- .../public/components/gauge_component.tsx | 166 ++++++++++++------ .../expression_renderers/gauge_renderer.tsx | 3 +- .../expression_gauge/public/plugin.ts | 6 +- .../expression_gauge/public/services/index.ts | 3 +- .../public/services/palette_service.ts | 13 ++ src/plugins/vis_types/gauge/public/to_ast.ts | 5 +- .../vis_types/gauge/public/utils/palette.ts | 2 +- 13 files changed, 170 insertions(+), 60 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_gauge/public/services/palette_service.ts diff --git a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/__snapshots__/gauge_function.test.ts.snap b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/__snapshots__/gauge_function.test.ts.snap index 3ff6f1608eb09..2eca3361d097d 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/__snapshots__/gauge_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/__snapshots__/gauge_function.test.ts.snap @@ -47,6 +47,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "arc", "ticksPosition": "auto", }, @@ -96,6 +97,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "arc", "ticksPosition": "auto", }, @@ -143,6 +145,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "auto", }, @@ -190,6 +193,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "auto", }, @@ -237,6 +241,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "bands", }, @@ -286,6 +291,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "circle", "ticksPosition": "auto", }, @@ -335,6 +341,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "circle", "ticksPosition": "auto", }, @@ -382,6 +389,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "auto", }, @@ -429,6 +437,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "hidden", }, @@ -476,6 +485,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "auto", }, @@ -523,6 +533,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "auto", }, @@ -570,6 +581,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "auto", }, @@ -617,6 +629,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "horizontalBullet", "ticksPosition": "auto", }, @@ -664,6 +677,7 @@ Object { "metric": "col-0-1", "min": "col-1-2", "palette": undefined, + "percentageMode": false, "shape": "verticalBullet", "ticksPosition": "auto", }, diff --git a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts index 80cfaff57c35c..fd681a27c4a6c 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/expression_functions/gauge_function.ts @@ -180,6 +180,14 @@ export const gaugeFunction = (): GaugeExpressionFunctionDefinition => ({ defaultMessage: 'Specifies the mode of centralMajor', }), }, + // used only in legacy gauge, consider it as @deprecated + percentageMode: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionGauge.functions.gauge.percentageMode.help', { + defaultMessage: 'Enables relative precentage mode', + }), + }, ariaLabel: { types: ['string'], help: i18n.translate('expressionGauge.functions.gaugeChart.config.ariaLabel.help', { diff --git a/src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts index e71b5b28c7dcf..3cd6d566d4870 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts @@ -45,6 +45,8 @@ export interface GaugeState { colorMode?: GaugeColorMode; palette?: PaletteOutput; shape: GaugeShape; + /** @deprecated This field is deprecated and going to be removed in the futher release versions. */ + percentageMode?: boolean; } export type GaugeArguments = GaugeState & { diff --git a/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts index 277071b416a13..9e72a0eabef65 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { ChartsPluginSetup } from '../../../../charts/public'; +import type { ChartsPluginSetup, PaletteRegistry } from '../../../../charts/public'; import type { IFieldFormat, SerializedFieldFormat } from '../../../../field_formats/common'; import type { GaugeExpressionProps } from './expression_functions'; @@ -15,6 +15,7 @@ export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; export type GaugeRenderProps = GaugeExpressionProps & { formatFactory: FormatFactory; chartsThemeService: ChartsPluginSetup['theme']; + paletteService: PaletteRegistry; }; export interface ColorStop { diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/__snapshots__/gauge_component.test.tsx.snap b/src/plugins/chart_expressions/expression_gauge/public/components/__snapshots__/gauge_component.test.tsx.snap index 0bd72c606dd9f..59aaa3677e9bc 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/__snapshots__/gauge_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_gauge/public/components/__snapshots__/gauge_component.test.tsx.snap @@ -41,6 +41,7 @@ exports[`GaugeComponent renders the chart 1`] = ` 4, ] } + tooltipValueFormatter={[Function]} /> `; diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx index 488d5f57a6458..c71b95a95d02c 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx @@ -54,6 +54,7 @@ jest.mock('@elastic/charts', () => { }); const chartsThemeService = chartPluginMock.createSetupContract().theme; +const paletteThemeService = chartPluginMock.createSetupContract().palettes; const formatService = fieldFormatsServiceMock.createStartContract(); const args: GaugeArguments = { labelMajor: 'Gauge', @@ -81,12 +82,13 @@ const createData = ( describe('GaugeComponent', function () { let wrapperProps: GaugeRenderProps; - beforeAll(() => { + beforeAll(async () => { wrapperProps = { data: createData(), chartsThemeService, args, formatFactory: formatService.deserialize, + paletteService: await paletteThemeService.getPalettes(), }; }); diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx index 22601ae409f62..dfb560a33b092 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx @@ -5,10 +5,10 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import React, { FC, memo } from 'react'; +import React, { FC, memo, useCallback } from 'react'; import { Chart, Goal, Settings } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { CustomPaletteState } from '../../../../charts/public'; +import type { CustomPaletteState, PaletteOutput } from '../../../../charts/public'; import { EmptyPlaceholder } from '../../../../charts/public'; import { isVisDimension } from '../../../../visualizations/common/utils'; import { @@ -42,37 +42,7 @@ declare global { } } -function normalizeColors( - { colors, stops, range, rangeMin, rangeMax }: CustomPaletteState, - min: number, - max: number -) { - if (!colors) { - return; - } - const colorsOutOfRangeSmaller = Math.max( - stops.filter((stop, i) => (range === 'percent' ? stop < 0 : stop < min)).length, - 0 - ); - let updatedColors = colors.slice(colorsOutOfRangeSmaller); - - let correctMin = rangeMin; - let correctMax = rangeMax; - if (range === 'percent') { - correctMin = min + rangeMin * ((max - min) / 100); - correctMax = min + rangeMax * ((max - min) / 100); - } - - if (correctMin > min && isFinite(correctMin)) { - updatedColors = [`rgba(255,255,255,0)`, ...updatedColors]; - } - - if (correctMax < max && isFinite(correctMax)) { - updatedColors = [...updatedColors, `rgba(255,255,255,0)`]; - } - - return updatedColors; -} +const TRANSPARENT = `rgba(255,255,255,0)`; function normalizeBands( { colors, stops, range, rangeMax, rangeMin }: CustomPaletteState, @@ -111,6 +81,28 @@ function normalizeBands( return [...firstRanges, ...orderedStops, ...lastRanges]; } +const toPercents = (min: number, max: number) => (v: number) => (v - min) / (max - min); + +function normalizeBandsLegacy({ colors, stops }: CustomPaletteState, value: number) { + const min = stops[0]; + const max = stops[stops.length - 1]; + const convertToPercents = toPercents(min, max); + const normalizedStops = stops.map(convertToPercents); + + if (max < value) { + normalizedStops.push(convertToPercents(value)); + } + + return normalizedStops; +} + +function actualValueToPercentsLegacy({ stops }: CustomPaletteState, value: number) { + const min = stops[0]; + const max = stops[stops.length - 1]; + const convertToPercents = toPercents(min, max); + return convertToPercents(value); +} + function getTitle( majorMode?: GaugeLabelMajorMode | GaugeCentralMajorMode, major?: string, @@ -144,7 +136,8 @@ function getTicksLabels(baseStops: number[]) { function getTicks( ticksPosition: GaugeTicksPosition, range: [number, number], - colorBands?: number[] + colorBands?: number[], + percentageMode?: boolean ) { if (ticksPosition === GaugeTicksPositions.HIDDEN) { return []; @@ -158,16 +151,40 @@ function getTicks( const min = Math.min(...(colorBands || []), ...range); const max = Math.max(...(colorBands || []), ...range); const step = (max - min) / TICKS_NO; - return [ + + const ticks = [ ...Array(TICKS_NO) .fill(null) .map((_, i) => Number((min + step * i).toFixed(2))), max, ]; + const convertToPercents = toPercents(min, max); + return percentageMode ? ticks.map(convertToPercents) : ticks; } +const calculateRealRangeValueMin = ( + relativeRangeValue: number, + { min, max }: { min: number; max: number } +) => { + if (isFinite(relativeRangeValue)) { + return relativeRangeValue * ((max - min) / 100); + } + return min; +}; + +const calculateRealRangeValueMax = ( + relativeRangeValue: number, + { min, max }: { min: number; max: number } +) => { + if (isFinite(relativeRangeValue)) { + return relativeRangeValue * ((max - min) / 100); + } + + return max; +}; + export const GaugeComponent: FC = memo( - ({ data, args, formatFactory, chartsThemeService }) => { + ({ data, args, formatFactory, paletteService, chartsThemeService }) => { const { shape: gaugeType, palette, @@ -179,6 +196,40 @@ export const GaugeComponent: FC = memo( centralMajorMode, ticksPosition, } = args; + + const getColor = useCallback( + ( + value, + paletteConfig: PaletteOutput, + bands: number[], + percentageMode?: boolean + ) => { + const { rangeMin, rangeMax, range }: CustomPaletteState = paletteConfig.params!; + const minRealValue = bands[0]; + const maxRealValue = bands[bands.length - 1]; + let min = rangeMin; + let max = rangeMax; + + let stops = paletteConfig.params?.stops ?? []; + + if (percentageMode) { + stops = bands.map((v) => v * 100); + } + + if (range === 'percent') { + const minMax = { min: minRealValue, max: maxRealValue }; + + min = calculateRealRangeValueMin(min, minMax); + max = calculateRealRangeValueMax(max, minMax); + } + + return paletteService + .get(paletteConfig?.name ?? 'custom') + .getColorForValue?.(value, { ...paletteConfig.params, stops }, { min, max }); + }, + [paletteService] + ); + const table = data; const accessors = getAccessorsFromArgs(args, table.columns); @@ -251,18 +302,23 @@ export const GaugeComponent: FC = memo( customMetricFormatParams ?? tableMetricFormatParams ?? defaultMetricFormatParams ); - const colors = palette?.params?.colors ? normalizeColors(palette.params, min, max) : undefined; - const bands: number[] = (palette?.params as CustomPaletteState) - ? normalizeBands(args.palette?.params as CustomPaletteState, { min, max }) + let bands: number[] = (palette?.params as CustomPaletteState) + ? normalizeBands(palette?.params as CustomPaletteState, { min, max }) : [min, max]; // TODO: format in charts - const formattedActual = Math.round(Math.min(Math.max(metricValue, min), max) * 1000) / 1000; - const goalConfig = getGoalConfig(gaugeType); - const totalTicks = getTicks(ticksPosition, [min, max], bands); + let actualValue = Math.round(Math.min(Math.max(metricValue, min), max) * 1000) / 1000; + const totalTicks = getTicks(ticksPosition, [min, max], bands, args.percentageMode); const ticks = gaugeType === GaugeShapes.CIRCLE ? totalTicks.slice(0, totalTicks.length - 1) : totalTicks; + if (args.percentageMode && palette?.params && palette?.params.stops?.length) { + bands = normalizeBandsLegacy(palette?.params as CustomPaletteState, actualValue); + actualValue = actualValueToPercentsLegacy(palette?.params as CustomPaletteState, actualValue); + } + + const goalConfig = getGoalConfig(gaugeType); + const labelMajorTitle = getTitle(labelMajorMode, labelMajor, metricColumn?.name); // added extra space for nice rendering @@ -271,7 +327,7 @@ export const GaugeComponent: FC = memo( const extraTitles = isRoundShape(gaugeType) ? { - centralMinor: tickFormatter.convert(metricValue), + centralMinor: tickFormatter.convert(actualValue), centralMajor: getTitle(centralMajorMode, centralMajor, metricColumn?.name), } : {}; @@ -289,21 +345,27 @@ export const GaugeComponent: FC = memo( subtype={getSubtypeByGaugeType(gaugeType)} base={bands[0]} target={goal && goal >= bands[0] && goal <= bands[bands.length - 1] ? goal : undefined} - actual={formattedActual} + actual={actualValue} tickValueFormatter={({ value: tickValue }) => tickFormatter.convert(tickValue)} + tooltipValueFormatter={(tooltipValue) => tickFormatter.convert(tooltipValue)} bands={bands} ticks={ticks} bandFillColor={ - colorMode === GaugeColorModes.PALETTE && colors + colorMode === GaugeColorModes.PALETTE ? (val) => { - const index = bands && bands.indexOf(val.value) - 1; - return colors && index >= 0 && colors[index] - ? colors[index] - : val.value <= bands[0] - ? colors[0] - : colors[colors.length - 1]; + // bands value is equal to the stop. The purpose of this value is coloring the previous section, which is smaller, then the band. + // So, the smaller value should be taken. For the first element -1, for the next - middle value of the previous section. + let value = val.value - 1; + const valueIndex = bands.indexOf(val.value); + if (valueIndex > 0) { + value = val.value - (bands[valueIndex] - bands[valueIndex - 1]) / 2; + } + + return args.palette + ? getColor(value, args.palette, bands, args.percentageMode) ?? TRANSPARENT + : TRANSPARENT; } - : () => `rgba(255,255,255,0)` + : () => TRANSPARENT } labelMajor={labelMajorTitle ? `${labelMajorTitle}${majorExtraSpaces}` : labelMajorTitle} labelMinor={labelMinor ? `${labelMinor}${minorExtraSpaces}` : ''} diff --git a/src/plugins/chart_expressions/expression_gauge/public/expression_renderers/gauge_renderer.tsx b/src/plugins/chart_expressions/expression_gauge/public/expression_renderers/gauge_renderer.tsx index f762ddd5458bf..ebd2aaa6fc948 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/expression_renderers/gauge_renderer.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/expression_renderers/gauge_renderer.tsx @@ -12,7 +12,7 @@ import { ThemeServiceStart } from '../../../../../core/public'; import { KibanaThemeProvider } from '../../../../kibana_react/public'; import { ExpressionRenderDefinition } from '../../../../expressions/common/expression_renderers'; import { EXPRESSION_GAUGE_NAME, GaugeExpressionProps } from '../../common'; -import { getFormatService, getThemeService } from '../services'; +import { getFormatService, getPaletteService, getThemeService } from '../services'; interface ExpressionGaugeRendererDependencies { theme: ThemeServiceStart; @@ -39,6 +39,7 @@ export const gaugeRenderer: ( {...config} formatFactory={getFormatService().deserialize} chartsThemeService={getThemeService()} + paletteService={getPaletteService()} />
, diff --git a/src/plugins/chart_expressions/expression_gauge/public/plugin.ts b/src/plugins/chart_expressions/expression_gauge/public/plugin.ts index 8769af43a03de..7cc6ebec0d5d8 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_gauge/public/plugin.ts @@ -9,7 +9,7 @@ import { ChartsPluginSetup } from '../../../charts/public'; import { CoreSetup, CoreStart } from '../../../../core/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; import { gaugeFunction } from '../common'; -import { setFormatService, setThemeService } from './services'; +import { setFormatService, setThemeService, setPaletteService } from './services'; import { gaugeRenderer } from './expression_renderers'; import type { FieldFormatsStart } from '../../../field_formats/public'; @@ -28,6 +28,10 @@ export interface ExpressionGaugePluginStart { export class ExpressionGaugePlugin { public setup(core: CoreSetup, { expressions, charts }: ExpressionGaugePluginSetup) { setThemeService(charts.theme); + charts.palettes.getPalettes().then((palettes) => { + setPaletteService(palettes); + }); + expressions.registerFunction(gaugeFunction); expressions.registerRenderer(gaugeRenderer({ theme: core.theme })); } diff --git a/src/plugins/chart_expressions/expression_gauge/public/services/index.ts b/src/plugins/chart_expressions/expression_gauge/public/services/index.ts index ef6cffcf6ec34..a4d0adc9f6a20 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/services/index.ts +++ b/src/plugins/chart_expressions/expression_gauge/public/services/index.ts @@ -7,4 +7,5 @@ */ export { getFormatService, setFormatService } from './format_service'; -export { setThemeService, getThemeService } from './theme_service'; +export { getThemeService, setThemeService } from './theme_service'; +export { getPaletteService, setPaletteService } from './palette_service'; diff --git a/src/plugins/chart_expressions/expression_gauge/public/services/palette_service.ts b/src/plugins/chart_expressions/expression_gauge/public/services/palette_service.ts new file mode 100644 index 0000000000000..cfcf2a818c5bc --- /dev/null +++ b/src/plugins/chart_expressions/expression_gauge/public/services/palette_service.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createGetterSetter } from '../../../../kibana_utils/public'; +import { PaletteRegistry } from '../../../../charts/public'; + +export const [getPaletteService, setPaletteService] = + createGetterSetter('palette'); diff --git a/src/plugins/vis_types/gauge/public/to_ast.ts b/src/plugins/vis_types/gauge/public/to_ast.ts index 041ae765b7696..dfb483d47fd21 100644 --- a/src/plugins/vis_types/gauge/public/to_ast.ts +++ b/src/plugins/vis_types/gauge/public/to_ast.ts @@ -71,6 +71,7 @@ export const toExpressionAst: VisToExpressionAst = (vis, params) colorMode: 'palette', centralMajorMode, ...(centralMajorMode === 'custom' ? { labelMinor: style.subText } : {}), + percentageMode, }); if (colorsRange && colorsRange.length) { @@ -80,8 +81,8 @@ export const toExpressionAst: VisToExpressionAst = (vis, params) range: percentageMode ? 'percent' : 'number', continuity: 'none', gradient: true, - rangeMax: percentageMode ? 100 : Infinity, - rangeMin: 0, + rangeMax: percentageMode ? 100 : stopsWithColors.stop[stopsWithColors.stop.length - 1], + rangeMin: stopsWithColors.stop[0], }); gauge.addArgument('palette', buildExpression([palette])); diff --git a/src/plugins/vis_types/gauge/public/utils/palette.ts b/src/plugins/vis_types/gauge/public/utils/palette.ts index a236a0daa6d53..ff3a4b10a0118 100644 --- a/src/plugins/vis_types/gauge/public/utils/palette.ts +++ b/src/plugins/vis_types/gauge/public/utils/palette.ts @@ -34,7 +34,7 @@ export const getStopsWithColorsFromRanges = ( ) => { return ranges.reduce( (acc, range, index, rangesArr) => { - if (index && range.from !== rangesArr[index - 1].to) { + if ((index && range.from !== rangesArr[index - 1].to) || index === 0) { acc.color.push(TRANSPARENT); acc.stop.push(range.from); } From 813ba8554f2926d56f03138c74c16acb22cfe218 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Tue, 8 Mar 2022 11:18:02 +0100 Subject: [PATCH 051/140] Fix copy and pasted renderer user_name test (#126663) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../body/renderers/user_name.test.tsx | 81 +++++-------------- .../timeline/body/renderers/user_name.tsx | 2 +- 2 files changed, 19 insertions(+), 64 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.test.tsx index d82e4c7d6719a..d81104ff5be03 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.test.tsx @@ -8,12 +8,12 @@ import React from 'react'; import { mount } from 'enzyme'; import { waitFor } from '@testing-library/react'; -import { HostName } from './host_name'; import { TestProviders } from '../../../../../common/mock'; import { TimelineId, TimelineTabs } from '../../../../../../common/types'; import { StatefulEventContext } from '../../../../../../../timelines/public'; import { timelineActions } from '../../../../store/timeline'; import { activeTimeline } from '../../../../containers/active_timeline_context'; +import { UserName } from './user_name'; jest.mock('react-redux', () => { const origin = jest.requireActual('react-redux'); @@ -51,14 +51,13 @@ jest.mock('../../../../store/timeline', () => { }; }); -// TODO USER NAME -describe('HostName', () => { +describe('UserName', () => { const props = { - fieldName: 'host.name', + fieldName: 'user.name', contextId: 'test-context-id', eventId: 'test-event-id', isDraggable: false, - value: 'Mock Host', + value: 'Mock User', }; let toggleExpandedDetail: jest.SpyInstance; @@ -70,16 +69,14 @@ describe('HostName', () => { afterEach(() => { toggleExpandedDetail.mockClear(); }); - test('should render host name', () => { + test('should render user name', () => { const wrapper = mount( - + ); - expect(wrapper.find('[data-test-subj="host-details-button"]').last().text()).toEqual( - props.value - ); + expect(wrapper.find('[data-test-subj="users-link-anchor"]').last().text()).toEqual(props.value); }); test('should render DefaultDraggable if isDraggable is true', () => { @@ -89,56 +86,14 @@ describe('HostName', () => { }; const wrapper = mount( - + ); expect(wrapper.find('[data-test-subj="DefaultDraggable"]').exists()).toEqual(true); }); - test('if not enableHostDetailsFlyout, should go to hostdetails page', async () => { - const wrapper = mount( - - - - ); - - wrapper.find('[data-test-subj="host-details-button"]').first().simulate('click'); - await waitFor(() => { - expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled(); - expect(toggleExpandedDetail).not.toHaveBeenCalled(); - }); - }); - - test('if enableHostDetailsFlyout, should open HostDetailsSidePanel', async () => { - const context = { - enableHostDetailsFlyout: true, - enableIpDetailsFlyout: true, - timelineID: TimelineId.active, - tabType: TimelineTabs.query, - }; - const wrapper = mount( - - - - - - ); - - wrapper.find('[data-test-subj="host-details-button"]').first().simulate('click'); - await waitFor(() => { - expect(timelineActions.toggleDetailPanel).toHaveBeenCalledWith({ - panelView: 'hostDetail', - params: { - hostName: props.value, - }, - tabType: context.tabType, - timelineId: context.timelineID, - }); - }); - }); - - test('if enableHostDetailsFlyout and timelineId equals to `timeline-1`, should call toggleExpandedDetail', async () => { + test('if timelineId equals to `timeline-1`, should call toggleExpandedDetail', async () => { const context = { enableHostDetailsFlyout: true, enableIpDetailsFlyout: true, @@ -148,23 +103,23 @@ describe('HostName', () => { const wrapper = mount( - + ); - wrapper.find('[data-test-subj="host-details-button"]').first().simulate('click'); + wrapper.find('[data-test-subj="users-link-anchor"]').first().simulate('click'); await waitFor(() => { expect(toggleExpandedDetail).toHaveBeenCalledWith({ - panelView: 'hostDetail', + panelView: 'userDetail', params: { - hostName: props.value, + userName: props.value, }, }); }); }); - test('if enableHostDetailsFlyout but timelineId not equals to `TimelineId.active`, should not call toggleExpandedDetail', async () => { + test('if timelineId not equals to `TimelineId.active`, should not call toggleExpandedDetail', async () => { const context = { enableHostDetailsFlyout: true, enableIpDetailsFlyout: true, @@ -174,17 +129,17 @@ describe('HostName', () => { const wrapper = mount( - + ); - wrapper.find('[data-test-subj="host-details-button"]').first().simulate('click'); + wrapper.find('[data-test-subj="users-link-anchor"]').first().simulate('click'); await waitFor(() => { expect(timelineActions.toggleDetailPanel).toHaveBeenCalledWith({ - panelView: 'hostDetail', + panelView: 'userDetail', params: { - hostName: props.value, + userName: props.value, }, tabType: context.tabType, timelineId: context.timelineID, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.tsx index fae2652284a6a..61f62ee3c0e29 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.tsx @@ -83,7 +83,7 @@ const UserNameComponent: React.FC = ({ ); // The below is explicitly defined this way as the onClick takes precedence when it and the href are both defined - // When this component is used outside of timeline/alerts table (i.e. in the flyout) we would still like it to link to the Host Details page + // When this component is used outside of timeline/alerts table (i.e. in the flyout) we would still like it to link to the User Details page const content = useMemo( () => ( Date: Tue, 8 Mar 2022 12:18:15 +0100 Subject: [PATCH 052/140] APM Synthtrace: Introduce new DSL using generators (#123454) Co-authored-by: Martijn Laarman Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- packages/elastic-apm-synthtrace/BUILD.bazel | 2 + packages/elastic-apm-synthtrace/src/index.ts | 1 + .../src/lib/apm/apm_fields.ts | 8 +- .../src/lib/apm/base_span.ts | 10 +- .../apm/client/apm_synthtrace_es_client.ts | 121 +++++++--- .../client/apm_synthtrace_kibana_client.ts | 43 ++++ .../lib/apm/defaults/get_observer_defaults.ts | 16 -- .../src/lib/apm/index.ts | 10 +- .../get_breakdown_metrics.ts | 7 +- .../get_span_destination_metrics.ts | 2 +- .../get_transaction_metrics.ts | 6 +- .../src/lib/apm/utils/aggregate.ts | 2 - .../apm_events_to_elasticsearch_output.ts | 60 ----- .../lib/apm/utils/get_apm_write_targets.ts | 26 ++- .../src/lib/interval.ts | 78 +++++-- .../src/lib/serializable.ts | 2 +- .../src/lib/span_generator.ts | 50 +++++ .../src/lib/span_generators_union.ts | 49 +++++ .../src/lib/span_iterable.ts | 46 ++++ .../src/lib/stack_monitoring/cluster_stats.ts | 4 +- .../src/lib/stack_monitoring/kibana_stats.ts | 6 +- .../stack_monitoring_fields.ts | 1 + .../src/lib/stream_processor.ts | 207 ++++++++++++++++++ .../src/lib/timerange.ts | 9 +- .../src/lib/utils/merge_iterable.ts | 37 ++++ .../src/lib/utils/to_elasticsearch_output.ts | 44 ---- .../src/scripts/examples/01_simple_trace.ts | 127 +++++------ .../src/scripts/examples/02_kibana_stats.ts | 21 +- .../src/scripts/examples/03_monitoring.ts | 43 +--- .../elastic-apm-synthtrace/src/scripts/run.ts | 85 +++++-- .../src/scripts/scenario.ts | 9 +- .../src/scripts/utils/get_common_services.ts | 20 +- .../src/scripts/utils/parse_run_cli_flags.ts | 8 +- .../utils/start_historical_data_upload.ts | 140 ++++-------- .../scripts/utils/start_live_data_upload.ts | 85 ++++--- .../src/scripts/utils/upload_events.ts | 66 ------ .../src/scripts/utils/upload_next_batch.js | 15 -- .../src/scripts/utils/upload_next_batch.ts | 95 -------- ...apm_events_to_elasticsearch_output.test.ts | 26 +-- .../test/scenarios/01_simple_trace.test.ts | 9 +- .../scenarios/02_transaction_metrics.test.ts | 52 ++--- .../03_span_destination_metrics.test.ts | 103 ++++----- .../scenarios/04_breakdown_metrics.test.ts | 100 +++++---- .../cypress/fixtures/synthtrace/opbeans.ts | 82 ++++--- .../read_only_user/errors/generate_data.ts | 46 ++-- .../header_filters/generate_data.ts | 36 ++- .../aws_lambda/generate_data.ts | 8 +- .../apm/ftr_e2e/cypress/plugins/index.ts | 13 +- x-pack/plugins/apm/ftr_e2e/synthtrace.ts | 3 +- .../common/synthtrace_es_client_service.ts | 3 +- .../tests/anomalies/anomaly_charts.spec.ts | 2 +- .../generate_data.ts | 60 ++--- .../tests/cold_start/generate_data.ts | 20 +- .../tests/data_view/static.spec.ts | 8 +- .../tests/dependencies/generate_data.ts | 2 +- .../tests/error_rate/service_apis.spec.ts | 16 +- .../tests/error_rate/service_maps.spec.ts | 16 +- .../tests/errors/error_group_list.spec.ts | 16 +- .../tests/errors/generate_data.ts | 38 ++-- .../tests/latency/service_apis.spec.ts | 8 +- .../tests/latency/service_maps.spec.ts | 8 +- .../observability_overview.spec.ts | 12 +- .../service_nodes/get_service_nodes.spec.ts | 2 +- .../instances_main_statistics.spec.ts | 8 +- .../services/error_groups/generate_data.ts | 16 +- .../get_service_node_metadata.spec.ts | 2 +- .../services/service_details/generate_data.ts | 8 +- .../services/service_icons/generate_data.ts | 2 +- .../tests/services/throughput.spec.ts | 12 +- .../tests/services/top_services.spec.ts | 18 +- .../throughput/dependencies_apis.spec.ts | 8 +- .../tests/throughput/service_apis.spec.ts | 8 +- .../tests/throughput/service_maps.spec.ts | 8 +- ...actions_groups_detailed_statistics.spec.ts | 8 +- 74 files changed, 1287 insertions(+), 1061 deletions(-) create mode 100644 packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts delete mode 100644 packages/elastic-apm-synthtrace/src/lib/apm/defaults/get_observer_defaults.ts rename packages/elastic-apm-synthtrace/src/lib/apm/{utils => processors}/get_breakdown_metrics.ts (96%) rename packages/elastic-apm-synthtrace/src/lib/apm/{utils => processors}/get_span_destination_metrics.ts (95%) rename packages/elastic-apm-synthtrace/src/lib/apm/{utils => processors}/get_transaction_metrics.ts (94%) delete mode 100644 packages/elastic-apm-synthtrace/src/lib/apm/utils/apm_events_to_elasticsearch_output.ts create mode 100644 packages/elastic-apm-synthtrace/src/lib/span_generator.ts create mode 100644 packages/elastic-apm-synthtrace/src/lib/span_generators_union.ts create mode 100644 packages/elastic-apm-synthtrace/src/lib/span_iterable.ts create mode 100644 packages/elastic-apm-synthtrace/src/lib/stream_processor.ts create mode 100644 packages/elastic-apm-synthtrace/src/lib/utils/merge_iterable.ts delete mode 100644 packages/elastic-apm-synthtrace/src/lib/utils/to_elasticsearch_output.ts delete mode 100644 packages/elastic-apm-synthtrace/src/scripts/utils/upload_events.ts delete mode 100644 packages/elastic-apm-synthtrace/src/scripts/utils/upload_next_batch.js delete mode 100644 packages/elastic-apm-synthtrace/src/scripts/utils/upload_next_batch.ts diff --git a/packages/elastic-apm-synthtrace/BUILD.bazel b/packages/elastic-apm-synthtrace/BUILD.bazel index 09406644f44b2..646b94891f7c2 100644 --- a/packages/elastic-apm-synthtrace/BUILD.bazel +++ b/packages/elastic-apm-synthtrace/BUILD.bazel @@ -32,6 +32,7 @@ RUNTIME_DEPS = [ "@npm//object-hash", "@npm//p-limit", "@npm//yargs", + "@npm//node-fetch", ] TYPES_DEPS = [ @@ -43,6 +44,7 @@ TYPES_DEPS = [ "@npm//@types/object-hash", "@npm//moment", "@npm//p-limit", + "@npm//@types/node-fetch", ] jsts_transpiler( diff --git a/packages/elastic-apm-synthtrace/src/index.ts b/packages/elastic-apm-synthtrace/src/index.ts index 381222ee10efc..0138a6525baf5 100644 --- a/packages/elastic-apm-synthtrace/src/index.ts +++ b/packages/elastic-apm-synthtrace/src/index.ts @@ -14,3 +14,4 @@ export { createLogger, LogLevel } from './lib/utils/create_logger'; export type { Fields } from './lib/entity'; export type { ApmException, ApmSynthtraceEsClient } from './lib/apm'; +export type { SpanIterable } from './lib/span_iterable'; diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/apm_fields.ts b/packages/elastic-apm-synthtrace/src/lib/apm/apm_fields.ts index 4afebf0352a6a..f117fc879c0e7 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/apm_fields.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/apm_fields.ts @@ -31,9 +31,14 @@ export type ApmUserAgentFields = Partial<{ export interface ApmException { message: string; } +export interface Observer { + version: string; + version_major: number; +} export type ApmFields = Fields & Partial<{ + 'timestamp.us'?: number; 'agent.name': string; 'agent.version': string; 'container.id': string; @@ -47,8 +52,7 @@ export type ApmFields = Fields & 'host.name': string; 'kubernetes.pod.uid': string; 'metricset.name': string; - 'observer.version': string; - 'observer.version_major': number; + observer: Observer; 'parent.id': string; 'processor.event': string; 'processor.name': string; diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/base_span.ts b/packages/elastic-apm-synthtrace/src/lib/apm/base_span.ts index 4fd5ee2698606..fa57c2871d8a8 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/base_span.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/base_span.ts @@ -24,7 +24,7 @@ export class BaseSpan extends Serializable { }); } - parent(span: BaseSpan) { + parent(span: BaseSpan): this { this.fields['trace.id'] = span.fields['trace.id']; this.fields['parent.id'] = span.isSpan() ? span.fields['span.id'] @@ -40,7 +40,7 @@ export class BaseSpan extends Serializable { return this; } - children(...children: BaseSpan[]) { + children(...children: BaseSpan[]): this { children.forEach((child) => { child.parent(this); }); @@ -50,17 +50,17 @@ export class BaseSpan extends Serializable { return this; } - success() { + success(): this { this.fields['event.outcome'] = 'success'; return this; } - failure() { + failure(): this { this.fields['event.outcome'] = 'failure'; return this; } - outcome(outcome: 'success' | 'failure' | 'unknown') { + outcome(outcome: 'success' | 'failure' | 'unknown'): this { this.fields['event.outcome'] = outcome; return this; } diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts b/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts index 4a25d7009ad01..f5d68d8614e6b 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_es_client.ts @@ -7,56 +7,115 @@ */ import { Client } from '@elastic/elasticsearch'; -import { uploadEvents } from '../../../scripts/utils/upload_events'; -import { Fields } from '../../entity'; import { cleanWriteTargets } from '../../utils/clean_write_targets'; -import { getBreakdownMetrics } from '../utils/get_breakdown_metrics'; -import { getSpanDestinationMetrics } from '../utils/get_span_destination_metrics'; -import { getTransactionMetrics } from '../utils/get_transaction_metrics'; import { getApmWriteTargets } from '../utils/get_apm_write_targets'; import { Logger } from '../../utils/create_logger'; -import { apmEventsToElasticsearchOutput } from '../utils/apm_events_to_elasticsearch_output'; +import { ApmFields } from '../apm_fields'; +import { SpanIterable } from '../../span_iterable'; +import { StreamProcessor } from '../../stream_processor'; +import { SpanGeneratorsUnion } from '../../span_generators_union'; + +export interface StreamToBulkOptions { + concurrency?: number; + maxDocs?: number; + mapToIndex?: (document: Record) => string; +} export class ApmSynthtraceEsClient { - constructor(private readonly client: Client, private readonly logger: Logger) {} + constructor( + private readonly client: Client, + private readonly logger: Logger, + private readonly forceDataStreams: boolean + ) {} private getWriteTargets() { - return getApmWriteTargets({ client: this.client }); + return getApmWriteTargets({ client: this.client, forceDataStreams: this.forceDataStreams }); } clean() { - return this.getWriteTargets().then((writeTargets) => - cleanWriteTargets({ + return this.getWriteTargets().then(async (writeTargets) => { + const indices = Object.values(writeTargets); + this.logger.info(`Attempting to clean: ${indices}`); + if (this.forceDataStreams) { + for (const name of indices) { + const dataStream = await this.client.indices.getDataStream({ name }, { ignore: [404] }); + if (dataStream.data_streams && dataStream.data_streams.length > 0) { + this.logger.debug(`Deleting datastream: ${name}`); + await this.client.indices.deleteDataStream({ name }); + } + } + return; + } + + return cleanWriteTargets({ client: this.client, - targets: Object.values(writeTargets), + targets: indices, logger: this.logger, - }) - ); + }); + }); } - async index(events: Fields[]) { - const writeTargets = await this.getWriteTargets(); + async updateComponentTemplates(numberOfPrimaryShards: number) { + const response = await this.client.cluster.getComponentTemplate({ name: '*apm*@custom' }); + for (const componentTemplate of response.component_templates) { + if (componentTemplate.component_template._meta?.package?.name !== 'apm') continue; - const eventsToIndex = apmEventsToElasticsearchOutput({ - events: [ - ...events, - ...getTransactionMetrics(events), - ...getSpanDestinationMetrics(events), - ...getBreakdownMetrics(events), - ], - writeTargets, - }); + componentTemplate.component_template.template.settings = { + index: { + number_of_shards: numberOfPrimaryShards, + }, + }; - await uploadEvents({ - batchSize: 1000, - client: this.client, - clientWorkers: 5, - events: eventsToIndex, - logger: this.logger, + const putTemplate = await this.client.cluster.putComponentTemplate({ + name: componentTemplate.name, + ...componentTemplate.component_template, + }); + this.logger.info( + `- updated component template ${componentTemplate.name}, acknowledged: ${putTemplate.acknowledged}` + ); + } + } + + async index(events: SpanIterable | SpanIterable[], options?: StreamToBulkOptions) { + const dataStream = Array.isArray(events) ? new SpanGeneratorsUnion(events) : events; + + const writeTargets = await this.getWriteTargets(); + // TODO logger.perf + await this.client.helpers.bulk({ + concurrency: options?.concurrency ?? 10, + refresh: false, + refreshOnCompletion: false, + datasource: new StreamProcessor({ + processors: StreamProcessor.apmProcessors, + maxSourceEvents: options?.maxDocs, + logger: this.logger, + }) + // TODO https://github.com/elastic/elasticsearch-js/issues/1610 + // having to map here is awkward, it'd be better to map just before serialization. + .streamToDocumentAsync(StreamProcessor.toDocument, dataStream), + onDrop: (doc) => { + this.logger.info(doc); + }, + // TODO bug in client not passing generic to BulkHelperOptions<> + // https://github.com/elastic/elasticsearch-js/issues/1611 + onDocument: (doc: unknown) => { + const d = doc as Record; + const index = options?.mapToIndex + ? options?.mapToIndex(d) + : this.forceDataStreams + ? StreamProcessor.getDataStreamForEvent(d, writeTargets) + : StreamProcessor.getIndexForEvent(d, writeTargets); + return { create: { _index: index } }; + }, }); + const indices = Object.values(writeTargets); + this.logger.info(`Indexed all data attempting to refresh: ${indices}`); + return this.client.indices.refresh({ - index: Object.values(writeTargets), + index: indices, + allow_no_indices: true, + ignore_unavailable: true, }); } } diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts b/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts new file mode 100644 index 0000000000000..6abd3e7633c6b --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import fetch from 'node-fetch'; +import { Logger } from '../../utils/create_logger'; + +export class ApmSynthtraceKibanaClient { + constructor(private readonly logger: Logger) {} + + async migrateCloudToManagedApm(cloudId: string, username: string, password: string) { + await this.logger.perf('migrate_apm_on_cloud', async () => { + this.logger.info('attempting to migrate cloud instance over to managed APM'); + const cloudUrls = Buffer.from(cloudId.split(':')[1], 'base64').toString().split('$'); + const kibanaCloudUrl = `https://${cloudUrls[2]}.${cloudUrls[0]}`; + const response = await fetch( + kibanaCloudUrl + '/internal/apm/fleet/cloud_apm_package_policy', + { + method: 'POST', // *GET, POST, PUT, DELETE, etc. + headers: { + Authorization: 'Basic ' + Buffer.from(username + ':' + password).toString('base64'), + Accept: 'application/json', + 'Content-Type': 'application/json', + 'kbn-xsrf': 'kibana', + }, + } + ); + const responseJson = await response.json(); + if (responseJson.message) { + this.logger.info(`Cloud Instance already migrated to managed APM: ${responseJson.message}`); + } + if (responseJson.cloudApmPackagePolicy) { + this.logger.info( + `Cloud Instance migrated to managed APM: ${responseJson.cloudApmPackagePolicy.package.version}` + ); + } + }); + } +} diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/defaults/get_observer_defaults.ts b/packages/elastic-apm-synthtrace/src/lib/apm/defaults/get_observer_defaults.ts deleted file mode 100644 index 882029a50e47f..0000000000000 --- a/packages/elastic-apm-synthtrace/src/lib/apm/defaults/get_observer_defaults.ts +++ /dev/null @@ -1,16 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { ApmFields } from '../apm_fields'; - -export function getObserverDefaults(): ApmFields { - return { - 'observer.version': '7.16.0', - 'observer.version_major': 7, - }; -} diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/index.ts b/packages/elastic-apm-synthtrace/src/lib/apm/index.ts index f020d9a1282e8..fcb8e078bf02a 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/index.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/index.ts @@ -7,12 +7,10 @@ */ import { service } from './service'; import { browser } from './browser'; -import { getTransactionMetrics } from './utils/get_transaction_metrics'; -import { getSpanDestinationMetrics } from './utils/get_span_destination_metrics'; -import { getObserverDefaults } from './defaults/get_observer_defaults'; +import { getTransactionMetrics } from './processors/get_transaction_metrics'; +import { getSpanDestinationMetrics } from './processors/get_span_destination_metrics'; import { getChromeUserAgentDefaults } from './defaults/get_chrome_user_agent_defaults'; -import { apmEventsToElasticsearchOutput } from './utils/apm_events_to_elasticsearch_output'; -import { getBreakdownMetrics } from './utils/get_breakdown_metrics'; +import { getBreakdownMetrics } from './processors/get_breakdown_metrics'; import { getApmWriteTargets } from './utils/get_apm_write_targets'; import { ApmSynthtraceEsClient } from './client/apm_synthtrace_es_client'; @@ -23,9 +21,7 @@ export const apm = { browser, getTransactionMetrics, getSpanDestinationMetrics, - getObserverDefaults, getChromeUserAgentDefaults, - apmEventsToElasticsearchOutput, getBreakdownMetrics, getApmWriteTargets, ApmSynthtraceEsClient, diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_breakdown_metrics.ts b/packages/elastic-apm-synthtrace/src/lib/apm/processors/get_breakdown_metrics.ts similarity index 96% rename from packages/elastic-apm-synthtrace/src/lib/apm/utils/get_breakdown_metrics.ts rename to packages/elastic-apm-synthtrace/src/lib/apm/processors/get_breakdown_metrics.ts index 4f29a31d5d278..1772b5f655713 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_breakdown_metrics.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/processors/get_breakdown_metrics.ts @@ -8,7 +8,7 @@ import objectHash from 'object-hash'; import { groupBy, pickBy } from 'lodash'; import { ApmFields } from '../apm_fields'; -import { createPicker } from './create_picker'; +import { createPicker } from '../utils/create_picker'; const instanceFields = [ 'container.*', @@ -41,7 +41,10 @@ export function getBreakdownMetrics(events: ApmFields[]) { Object.keys(txWithSpans).forEach((transactionId) => { const txEvents = txWithSpans[transactionId]; - const transaction = txEvents.find((event) => event['processor.event'] === 'transaction')!; + const transaction = txEvents.find((event) => event['processor.event'] === 'transaction'); + if (transaction === undefined) { + return; + } const eventsById: Record = {}; const activityByParentId: Record> = {}; diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_span_destination_metrics.ts b/packages/elastic-apm-synthtrace/src/lib/apm/processors/get_span_destination_metrics.ts similarity index 95% rename from packages/elastic-apm-synthtrace/src/lib/apm/utils/get_span_destination_metrics.ts rename to packages/elastic-apm-synthtrace/src/lib/apm/processors/get_span_destination_metrics.ts index 7adcdaa6ff940..b806948a0949b 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_span_destination_metrics.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/processors/get_span_destination_metrics.ts @@ -7,7 +7,7 @@ */ import { ApmFields } from '../apm_fields'; -import { aggregate } from './aggregate'; +import { aggregate } from '../utils/aggregate'; export function getSpanDestinationMetrics(events: ApmFields[]) { const exitSpans = events.filter((event) => !!event['span.destination.service.resource']); diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_transaction_metrics.ts b/packages/elastic-apm-synthtrace/src/lib/apm/processors/get_transaction_metrics.ts similarity index 94% rename from packages/elastic-apm-synthtrace/src/lib/apm/utils/get_transaction_metrics.ts rename to packages/elastic-apm-synthtrace/src/lib/apm/processors/get_transaction_metrics.ts index baa9f57a19a46..c5d8de7d42998 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_transaction_metrics.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/processors/get_transaction_metrics.ts @@ -8,7 +8,7 @@ import { sortBy } from 'lodash'; import { ApmFields } from '../apm_fields'; -import { aggregate } from './aggregate'; +import { aggregate } from '../utils/aggregate'; function sortAndCompressHistogram(histogram?: { values: number[]; counts: number[] }) { return sortBy(histogram?.values).reduce( @@ -34,12 +34,13 @@ export function getTransactionMetrics(events: ApmFields[]) { .map((transaction) => { return { ...transaction, - ['trace.root']: transaction['parent.id'] === undefined, + ['transaction.root']: transaction['parent.id'] === undefined, }; }); const metricsets = aggregate(transactions, [ 'trace.root', + 'transaction.root', 'transaction.name', 'transaction.type', 'event.outcome', @@ -77,7 +78,6 @@ export function getTransactionMetrics(events: ApmFields[]) { histogram.counts.push(1); histogram.values.push(Number(transaction['transaction.duration.us'])); } - return { ...metricset.key, 'metricset.name': 'transaction', diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/utils/aggregate.ts b/packages/elastic-apm-synthtrace/src/lib/apm/utils/aggregate.ts index 505f7452fe5d9..0a57debc5922e 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/utils/aggregate.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/utils/aggregate.ts @@ -29,7 +29,6 @@ export function aggregate(events: ApmFields[], fields: string[]) { const id = objectHash(key); let metricset = metricsets.get(id); - if (!metricset) { metricset = { key: { ...key, 'processor.event': 'metric', 'processor.name': 'metric' }, @@ -37,7 +36,6 @@ export function aggregate(events: ApmFields[], fields: string[]) { }; metricsets.set(id, metricset); } - metricset.events.push(event); } diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/utils/apm_events_to_elasticsearch_output.ts b/packages/elastic-apm-synthtrace/src/lib/apm/utils/apm_events_to_elasticsearch_output.ts deleted file mode 100644 index 46456098df4a0..0000000000000 --- a/packages/elastic-apm-synthtrace/src/lib/apm/utils/apm_events_to_elasticsearch_output.ts +++ /dev/null @@ -1,60 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { getObserverDefaults } from '../defaults/get_observer_defaults'; -import { ApmFields } from '../apm_fields'; -import { dedot } from '../../utils/dedot'; -import { ElasticsearchOutput } from '../../utils/to_elasticsearch_output'; - -export interface ApmElasticsearchOutputWriteTargets { - transaction: string; - span: string; - error: string; - metric: string; -} - -const observerDefaults = getObserverDefaults(); - -const esDocumentDefaults = { - ecs: { - version: '1.4', - }, -}; - -dedot(observerDefaults, esDocumentDefaults); - -export function apmEventsToElasticsearchOutput({ - events, - writeTargets, -}: { - events: ApmFields[]; - writeTargets: ApmElasticsearchOutputWriteTargets; -}): ElasticsearchOutput[] { - return events.map((event) => { - const values = {}; - - Object.assign(values, event, { - '@timestamp': new Date(event['@timestamp']!).toISOString(), - 'timestamp.us': event['@timestamp']! * 1000, - 'service.node.name': - event['service.node.name'] || event['container.id'] || event['host.name'], - }); - - const document = {}; - - Object.assign(document, esDocumentDefaults); - - dedot(values, document); - - return { - _index: writeTargets[event['processor.event'] as keyof ApmElasticsearchOutputWriteTargets], - _source: document, - timestamp: event['@timestamp']!, - }; - }); -} diff --git a/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_apm_write_targets.ts b/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_apm_write_targets.ts index f040ca46a9db9..ec2e675d415fa 100644 --- a/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_apm_write_targets.ts +++ b/packages/elastic-apm-synthtrace/src/lib/apm/utils/get_apm_write_targets.ts @@ -7,13 +7,32 @@ */ import { Client } from '@elastic/elasticsearch'; -import { ApmElasticsearchOutputWriteTargets } from './apm_events_to_elasticsearch_output'; + +export interface ApmElasticsearchOutputWriteTargets { + transaction: string; + span: string; + error: string; + metric: string; + app_metric: string; +} export async function getApmWriteTargets({ client, + forceDataStreams, }: { client: Client; + forceDataStreams?: boolean; }): Promise { + if (forceDataStreams) { + return { + transaction: 'traces-apm-default', + span: 'traces-apm-default', + metric: 'metrics-apm.internal-default', + app_metric: 'metrics-apm.app-default', + error: 'logs-apm.error-default', + }; + } + const [indicesResponse, datastreamsResponse] = await Promise.all([ client.indices.getAlias({ index: 'apm-*', @@ -40,11 +59,12 @@ export async function getApmWriteTargets({ .find(({ key, writeIndexAlias }) => writeIndexAlias && key.includes(filter)) ?.writeIndexAlias!; } - + const metricsTarget = getDataStreamName('metrics-apm') || getAlias('-metric'); const targets = { transaction: getDataStreamName('traces-apm') || getAlias('-transaction'), span: getDataStreamName('traces-apm') || getAlias('-span'), - metric: getDataStreamName('metrics-apm') || getAlias('-metric'), + metric: metricsTarget, + app_metric: metricsTarget, error: getDataStreamName('logs-apm') || getAlias('-error'), }; diff --git a/packages/elastic-apm-synthtrace/src/lib/interval.ts b/packages/elastic-apm-synthtrace/src/lib/interval.ts index bafd1a06c5348..fc31d0290968a 100644 --- a/packages/elastic-apm-synthtrace/src/lib/interval.ts +++ b/packages/elastic-apm-synthtrace/src/lib/interval.ts @@ -6,27 +6,69 @@ * Side Public License, v 1. */ import moment from 'moment'; +import { ApmFields } from './apm/apm_fields'; +import { SpanIterable } from './span_iterable'; +import { SpanGenerator } from './span_generator'; -export class Interval { +export function parseInterval(interval: string): [number, string] { + const args = interval.match(/(\d+)(s|m|h|d)/); + if (!args || args.length < 3) { + throw new Error('Failed to parse interval'); + } + return [Number(args[1]), args[2] as any]; +} + +export class Interval implements Iterable { constructor( - private readonly from: number, - private readonly to: number, - private readonly interval: string - ) {} + public readonly from: Date, + public readonly to: Date, + public readonly interval: string, + public readonly yieldRate: number = 1 + ) { + [this.intervalAmount, this.intervalUnit] = parseInterval(interval); + } - rate(rate: number) { - let now = this.from; - const args = this.interval.match(/(.*)(s|m|h|d)/); - if (!args) { - throw new Error('Failed to parse interval'); - } - const timestamps: number[] = []; - while (now < this.to) { - timestamps.push(...new Array(rate).fill(now)); - now = moment(now) - .add(Number(args[1]), args[2] as any) - .valueOf(); + private readonly intervalAmount: number; + private readonly intervalUnit: any; + + spans(map: (timestamp: number, index?: number) => ApmFields[]): SpanIterable { + return new SpanGenerator(this, [ + function* (i) { + let index = 0; + for (const x of i) { + for (const a of map(x, index)) { + yield a; + index++; + } + } + }, + ]); + } + + rate(rate: number): Interval { + return new Interval(this.from, this.to, this.interval, rate); + } + private yieldRateTimestamps(timestamp: number) { + return new Array(this.yieldRate).fill(timestamp); + } + + private *_generate(): Iterable { + if (this.from > this.to) { + let now = this.from; + do { + yield* this.yieldRateTimestamps(now.getTime()); + now = new Date(moment(now).subtract(this.intervalAmount, this.intervalUnit).valueOf()); + } while (now > this.to); + } else { + let now = this.from; + do { + yield* this.yieldRateTimestamps(now.getTime()); + now = new Date(moment(now).add(this.intervalAmount, this.intervalUnit).valueOf()); + } while (now < this.to); } - return timestamps; + } + + [Symbol.iterator]() { + return this._generate()[Symbol.iterator](); } } diff --git a/packages/elastic-apm-synthtrace/src/lib/serializable.ts b/packages/elastic-apm-synthtrace/src/lib/serializable.ts index e9ffe3ae96994..1211098519dab 100644 --- a/packages/elastic-apm-synthtrace/src/lib/serializable.ts +++ b/packages/elastic-apm-synthtrace/src/lib/serializable.ts @@ -15,7 +15,7 @@ export class Serializable extends Entity { }); } - timestamp(time: number) { + timestamp(time: number): this { this.fields['@timestamp'] = time; return this; } diff --git a/packages/elastic-apm-synthtrace/src/lib/span_generator.ts b/packages/elastic-apm-synthtrace/src/lib/span_generator.ts new file mode 100644 index 0000000000000..aa043e982b503 --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/lib/span_generator.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Interval } from './interval'; +import { ApmFields } from './apm/apm_fields'; +import { SpanGeneratorsUnion } from './span_generators_union'; +import { SpanIterable } from './span_iterable'; + +export class SpanGenerator implements SpanIterable { + constructor( + private readonly interval: Interval, + private readonly dataGenerator: Array<(interval: Interval) => Generator> + ) { + this._order = interval.from > interval.to ? 'desc' : 'asc'; + } + + private readonly _order: 'desc' | 'asc'; + order() { + return this._order; + } + + toArray(): ApmFields[] { + return Array.from(this); + } + + concat(...iterables: SpanGenerator[]) { + return new SpanGeneratorsUnion([this, ...iterables]); + } + + *[Symbol.iterator]() { + for (const iterator of this.dataGenerator) { + for (const fields of iterator(this.interval)) { + yield fields; + } + } + } + + async *[Symbol.asyncIterator]() { + for (const iterator of this.dataGenerator) { + for (const fields of iterator(this.interval)) { + yield fields; + } + } + } +} diff --git a/packages/elastic-apm-synthtrace/src/lib/span_generators_union.ts b/packages/elastic-apm-synthtrace/src/lib/span_generators_union.ts new file mode 100644 index 0000000000000..9bbea307276c0 --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/lib/span_generators_union.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ApmFields } from './apm/apm_fields'; +import { SpanIterable } from './span_iterable'; +import { merge } from './utils/merge_iterable'; + +export class SpanGeneratorsUnion implements SpanIterable { + constructor(private readonly dataGenerators: SpanIterable[]) { + const orders = new Set<'desc' | 'asc'>(dataGenerators.map((d) => d.order())); + if (orders.size > 1) throw Error('Can only combine intervals with the same order()'); + this._order = orders.has('asc') ? 'asc' : 'desc'; + } + + static empty: SpanGeneratorsUnion = new SpanGeneratorsUnion([]); + + private readonly _order: 'desc' | 'asc'; + order() { + return this._order; + } + + toArray(): ApmFields[] { + return Array.from(this); + } + + concat(...iterables: SpanIterable[]) { + return new SpanGeneratorsUnion([...this.dataGenerators, ...iterables]); + } + + *[Symbol.iterator]() { + const iterator = merge(this.dataGenerators); + for (const fields of iterator) { + yield fields; + } + } + + async *[Symbol.asyncIterator]() { + for (const iterator of this.dataGenerators) { + for (const fields of iterator) { + yield fields; + } + } + } +} diff --git a/packages/elastic-apm-synthtrace/src/lib/span_iterable.ts b/packages/elastic-apm-synthtrace/src/lib/span_iterable.ts new file mode 100644 index 0000000000000..f40658feba1f2 --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/lib/span_iterable.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ApmFields } from './apm/apm_fields'; +import { SpanGeneratorsUnion } from './span_generators_union'; + +export interface SpanIterable extends Iterable, AsyncIterable { + order(): 'desc' | 'asc'; + + toArray(): ApmFields[]; + + concat(...iterables: SpanIterable[]): SpanGeneratorsUnion; +} + +export class SpanArrayIterable implements SpanIterable { + constructor(private fields: ApmFields[]) { + const timestamps = fields.filter((f) => f['@timestamp']).map((f) => f['@timestamp']!); + this._order = timestamps.length > 1 ? (timestamps[0] > timestamps[1] ? 'desc' : 'asc') : 'asc'; + } + + private readonly _order: 'desc' | 'asc'; + order() { + return this._order; + } + + async *[Symbol.asyncIterator](): AsyncIterator { + return this.fields[Symbol.iterator](); + } + + [Symbol.iterator](): Iterator { + return this.fields[Symbol.iterator](); + } + + concat(...iterables: SpanIterable[]): SpanGeneratorsUnion { + return new SpanGeneratorsUnion([this, ...iterables]); + } + + toArray(): ApmFields[] { + return this.fields; + } +} diff --git a/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/cluster_stats.ts b/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/cluster_stats.ts index 0995013cbcbb7..1505303e6b83c 100644 --- a/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/cluster_stats.ts +++ b/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/cluster_stats.ts @@ -17,13 +17,13 @@ export class ClusterStats extends Serializable { this.fields['license.status'] = 'active'; } - timestamp(timestamp: number) { + timestamp(timestamp: number): this { super.timestamp(timestamp); this.fields['cluster_stats.timestamp'] = new Date(timestamp).toISOString(); return this; } - indices(count: number) { + indices(count: number): this { this.fields['cluster_stats.indices.count'] = count; return this; } diff --git a/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/kibana_stats.ts b/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/kibana_stats.ts index 495e5f013600e..96df653119fd2 100644 --- a/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/kibana_stats.ts +++ b/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/kibana_stats.ts @@ -10,15 +10,15 @@ import { Serializable } from '../serializable'; import { StackMonitoringFields } from './stack_monitoring_fields'; export class KibanaStats extends Serializable { - timestamp(timestamp: number) { - super.timestamp(timestamp); + timestamp(timestamp: number): this { this.fields['kibana_stats.timestamp'] = new Date(timestamp).toISOString(); this.fields['kibana_stats.response_times.max'] = 250; this.fields['kibana_stats.kibana.status'] = 'green'; + this.fields.timestamp = timestamp; return this; } - requests(disconnects: number, total: number) { + requests(disconnects: number, total: number): this { this.fields['kibana_stats.requests.disconnects'] = disconnects; this.fields['kibana_stats.requests.total'] = total; return this; diff --git a/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/stack_monitoring_fields.ts b/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/stack_monitoring_fields.ts index 3e80d1e9f733f..1d79b9b014110 100644 --- a/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/stack_monitoring_fields.ts +++ b/packages/elastic-apm-synthtrace/src/lib/stack_monitoring/stack_monitoring_fields.ts @@ -26,4 +26,5 @@ export type StackMonitoringFields = Fields & 'kibana_stats.requests.total': number; 'kibana_stats.timestamp': string; 'kibana_stats.response_times.max': number; + timestamp: number; }>; diff --git a/packages/elastic-apm-synthtrace/src/lib/stream_processor.ts b/packages/elastic-apm-synthtrace/src/lib/stream_processor.ts new file mode 100644 index 0000000000000..35cb8d05582d6 --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/lib/stream_processor.ts @@ -0,0 +1,207 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import moment from 'moment'; +import { ApmFields } from './apm/apm_fields'; +import { SpanIterable } from './span_iterable'; +import { getTransactionMetrics } from './apm/processors/get_transaction_metrics'; +import { getSpanDestinationMetrics } from './apm/processors/get_span_destination_metrics'; +import { getBreakdownMetrics } from './apm/processors/get_breakdown_metrics'; +import { parseInterval } from './interval'; +import { dedot } from './utils/dedot'; +import { ApmElasticsearchOutputWriteTargets } from './apm/utils/get_apm_write_targets'; +import { Logger } from './utils/create_logger'; + +export interface StreamProcessorOptions { + processors: Array<(events: ApmFields[]) => ApmFields[]>; + flushInterval?: string; + maxBufferSize?: number; + // the maximum source events to process, not the maximum documents outputted by the processor + maxSourceEvents?: number; + logger?: Logger; +} + +export class StreamProcessor { + public static readonly apmProcessors = [ + getTransactionMetrics, + getSpanDestinationMetrics, + getBreakdownMetrics, + ]; + + constructor(private readonly options: StreamProcessorOptions) { + [this.intervalAmount, this.intervalUnit] = this.options.flushInterval + ? parseInterval(this.options.flushInterval) + : parseInterval('1m'); + } + private readonly intervalAmount: number; + private readonly intervalUnit: any; + + // TODO move away from chunking and feed this data one by one to processors + *stream(...eventSources: SpanIterable[]) { + const maxBufferSize = this.options.maxBufferSize ?? 10000; + const maxSourceEvents = this.options.maxSourceEvents; + let localBuffer = []; + let flushAfter: number | null = null; + let sourceEventsYielded = 0; + for (const eventSource of eventSources) { + const order = eventSource.order(); + this.options.logger?.info(`order: ${order}`); + for (const event of eventSource) { + const eventDate = event['@timestamp'] as number; + localBuffer.push(event); + if (flushAfter === null && eventDate !== null) { + flushAfter = this.calculateFlushAfter(eventDate, order); + } + + yield StreamProcessor.enrich(event); + sourceEventsYielded++; + if (maxSourceEvents && sourceEventsYielded % (maxSourceEvents / 10) === 0) { + this.options.logger?.info(`Yielded ${sourceEventsYielded} events`); + } + if (maxSourceEvents && sourceEventsYielded >= maxSourceEvents) { + // yielded the maximum source events, we still want the local buffer to generate derivative documents + break; + } + if ( + localBuffer.length === maxBufferSize || + (flushAfter != null && + ((order === 'asc' && eventDate > flushAfter) || + (order === 'desc' && eventDate < flushAfter))) + ) { + const e = new Date(eventDate).toISOString(); + const f = new Date(flushAfter!).toISOString(); + this.options.logger?.debug( + `flush ${localBuffer.length} documents ${order}: ${e} => ${f}` + ); + for (const processor of this.options.processors) { + yield* processor(localBuffer).map(StreamProcessor.enrich); + } + localBuffer = []; + flushAfter = this.calculateFlushAfter(flushAfter, order); + } + } + if (maxSourceEvents && sourceEventsYielded >= maxSourceEvents) { + this.options.logger?.info(`Yielded maximum number of documents: ${maxSourceEvents}`); + break; + } + } + if (localBuffer.length > 0) { + this.options.logger?.info(`Processing remaining buffer: ${localBuffer.length} items left`); + for (const processor of this.options.processors) { + yield* processor(localBuffer).map(StreamProcessor.enrich); + } + } + } + + private calculateFlushAfter(eventDate: number | null, order: 'asc' | 'desc') { + if (order === 'desc') { + return moment(eventDate).subtract(this.intervalAmount, this.intervalUnit).valueOf(); + } else { + return moment(eventDate).add(this.intervalAmount, this.intervalUnit).valueOf(); + } + } + + async *streamAsync(...eventSources: SpanIterable[]): AsyncIterator { + yield* this.stream(...eventSources); + } + *streamToDocument( + map: (d: ApmFields) => TDocument, + ...eventSources: SpanIterable[] + ): Generator { + for (const apmFields of this.stream(...eventSources)) { + yield map(apmFields); + } + } + async *streamToDocumentAsync( + map: (d: ApmFields) => TDocument, + ...eventSources: SpanIterable[] + ): AsyncIterator { + for (const apmFields of this.stream(...eventSources)) { + yield map(apmFields); + } + } + streamToArray(...eventSources: SpanIterable[]) { + return Array.from(this.stream(...eventSources)); + } + + static enrich(document: ApmFields): ApmFields { + // see https://github.com/elastic/apm-server/issues/7088 can not be provided as flat key/values + document.observer = { + version: '8.0.0', + version_major: 8, + }; + document['service.node.name'] = + document['service.node.name'] || document['container.id'] || document['host.name']; + document['ecs.version'] = '1.4'; + // TODO this non standard field should not be enriched here + if (document['processor.event'] !== 'metric') { + document['timestamp.us'] = document['@timestamp']! * 1000; + } + return document; + } + + static toDocument(document: ApmFields): Record { + if (!document.observer) { + document = StreamProcessor.enrich(document); + } + const newDoc: Record = {}; + dedot(document, newDoc); + if (typeof newDoc['@timestamp'] === 'number') { + const timestamp = newDoc['@timestamp']; + newDoc['@timestamp'] = new Date(timestamp).toISOString(); + } + return newDoc; + } + + static getDataStreamForEvent( + d: Record, + writeTargets: ApmElasticsearchOutputWriteTargets + ) { + if (!d.processor?.event) { + throw Error("'processor.event' is not set on document, can not determine target index"); + } + const eventType = d.processor.event as keyof ApmElasticsearchOutputWriteTargets; + let dataStream = writeTargets[eventType]; + if (eventType === 'metric') { + if (!d.service?.name) { + dataStream = 'metrics-apm.app-default'; + } else { + if (!d.transaction && !d.span) { + dataStream = 'metrics-apm.app-default'; + } + } + } + return dataStream; + } + + static getIndexForEvent( + d: Record, + writeTargets: ApmElasticsearchOutputWriteTargets + ) { + if (!d.processor?.event) { + throw Error("'processor.event' is not set on document, can not determine target index"); + } + + const eventType = d.processor.event as keyof ApmElasticsearchOutputWriteTargets; + return writeTargets[eventType]; + } +} + +export async function* streamProcessAsync( + processors: Array<(events: ApmFields[]) => ApmFields[]>, + ...eventSources: SpanIterable[] +) { + return new StreamProcessor({ processors }).streamAsync(...eventSources); +} + +export function streamProcessToArray( + processors: Array<(events: ApmFields[]) => ApmFields[]>, + ...eventSources: SpanIterable[] +) { + return new StreamProcessor({ processors }).streamToArray(...eventSources); +} diff --git a/packages/elastic-apm-synthtrace/src/lib/timerange.ts b/packages/elastic-apm-synthtrace/src/lib/timerange.ts index 14111ad7b8495..95e998e9ca20f 100644 --- a/packages/elastic-apm-synthtrace/src/lib/timerange.ts +++ b/packages/elastic-apm-synthtrace/src/lib/timerange.ts @@ -9,13 +9,16 @@ import { Interval } from './interval'; export class Timerange { - constructor(private from: number, private to: number) {} + constructor(private from: Date, private to: Date) {} interval(interval: string) { return new Interval(this.from, this.to, interval); } } -export function timerange(from: number, to: number) { - return new Timerange(from, to); +export function timerange(from: Date | number, to: Date | number) { + return new Timerange( + from instanceof Date ? from : new Date(from), + to instanceof Date ? to : new Date(to) + ); } diff --git a/packages/elastic-apm-synthtrace/src/lib/utils/merge_iterable.ts b/packages/elastic-apm-synthtrace/src/lib/utils/merge_iterable.ts new file mode 100644 index 0000000000000..415aa7eccfa1a --- /dev/null +++ b/packages/elastic-apm-synthtrace/src/lib/utils/merge_iterable.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ApmFields } from '../apm/apm_fields'; +import { SpanIterable } from '../span_iterable'; + +export function merge(iterables: SpanIterable[]): Iterable { + if (iterables.length === 1) return iterables[0]; + + const iterators = iterables.map>((i) => { + return i[Symbol.iterator](); + }); + let done = false; + const myIterable: Iterable = { + *[Symbol.iterator]() { + do { + const items = iterators.map((i) => i.next()); + done = items.every((item) => item.done); + if (!done) { + yield* items.filter((i) => !i.done).map((i) => i.value); + } + } while (!done); + // Done for the first time: close all iterators + for (const iterator of iterators) { + if (typeof iterator.return === 'function') { + iterator.return(); + } + } + }, + }; + return myIterable; +} diff --git a/packages/elastic-apm-synthtrace/src/lib/utils/to_elasticsearch_output.ts b/packages/elastic-apm-synthtrace/src/lib/utils/to_elasticsearch_output.ts deleted file mode 100644 index 58bafffaff692..0000000000000 --- a/packages/elastic-apm-synthtrace/src/lib/utils/to_elasticsearch_output.ts +++ /dev/null @@ -1,44 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Fields } from '../entity'; -import { dedot } from './dedot'; - -export interface ElasticsearchOutput { - _index: string; - _source: unknown; - timestamp: number; -} - -export function eventsToElasticsearchOutput({ - events, - writeTarget, -}: { - events: Fields[]; - writeTarget: string; -}): ElasticsearchOutput[] { - return events.map((event) => { - const values = {}; - - const timestamp = event['@timestamp']!; - - Object.assign(values, event, { - '@timestamp': new Date(timestamp).toISOString(), - }); - - const document = {}; - - dedot(values, document); - - return { - _index: writeTarget, - _source: document, - timestamp, - }; - }); -} diff --git a/packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts b/packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts index 4ea1af15f43ef..b8792f6e1753c 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts @@ -7,16 +7,15 @@ */ import { apm, timerange } from '../../index'; -import { apmEventsToElasticsearchOutput } from '../../lib/apm/utils/apm_events_to_elasticsearch_output'; -import { getApmWriteTargets } from '../../lib/apm/utils/get_apm_write_targets'; +import { Instance } from '../../lib/apm/instance'; import { Scenario } from '../scenario'; import { getCommonServices } from '../utils/get_common_services'; +import { RunOptions } from '../utils/parse_run_cli_flags'; -const scenario: Scenario = async ({ target, logLevel, scenarioOpts }) => { - const { client, logger } = getCommonServices({ target, logLevel }); - const writeTargets = await getApmWriteTargets({ client }); +const scenario: Scenario = async (runOptions: RunOptions) => { + const { logger } = getCommonServices(runOptions); - const { numServices = 3 } = scenarioOpts || {}; + const { numServices = 3 } = runOptions.scenarioOpts || {}; return { generate: ({ from, to }) => { @@ -28,79 +27,65 @@ const scenario: Scenario = async ({ target, logLevel, scenarioOpts }) => { const failedTimestamps = range.interval('1s').rate(1); - return new Array(numServices).fill(undefined).flatMap((_, index) => { - const events = logger.perf('generating_apm_events', () => { - const instance = apm - .service(`opbeans-go-${index}`, 'production', 'go') - .instance('instance'); + const instances = [...Array(numServices).keys()].map((index) => + apm.service(`opbeans-go-${index}`, 'production', 'go').instance('instance') + ); + const instanceSpans = (instance: Instance) => { + const successfulTraceEvents = successfulTimestamps.spans((timestamp) => + instance + .transaction(transactionName) + .timestamp(timestamp) + .duration(1000) + .success() + .children( + instance + .span('GET apm-*/_search', 'db', 'elasticsearch') + .duration(1000) + .success() + .destination('elasticsearch') + .timestamp(timestamp), + instance + .span('custom_operation', 'custom') + .duration(100) + .success() + .timestamp(timestamp) + ) + .serialize() + ); - const successfulTraceEvents = successfulTimestamps.flatMap((timestamp) => - instance - .transaction(transactionName) - .timestamp(timestamp) - .duration(1000) - .success() - .children( - instance - .span('GET apm-*/_search', 'db', 'elasticsearch') - .duration(1000) - .success() - .destination('elasticsearch') - .timestamp(timestamp), - instance - .span('custom_operation', 'custom') - .duration(100) - .success() - .timestamp(timestamp) - ) - .serialize() - ); + const failedTraceEvents = failedTimestamps.spans((timestamp) => + instance + .transaction(transactionName) + .timestamp(timestamp) + .duration(1000) + .failure() + .errors( + instance.error('[ResponseError] index_not_found_exception').timestamp(timestamp + 50) + ) + .serialize() + ); - const failedTraceEvents = failedTimestamps.flatMap((timestamp) => + const metricsets = range + .interval('30s') + .rate(1) + .spans((timestamp) => instance - .transaction(transactionName) + .appMetrics({ + 'system.memory.actual.free': 800, + 'system.memory.total': 1000, + 'system.cpu.total.norm.pct': 0.6, + 'system.process.cpu.total.norm.pct': 0.7, + }) .timestamp(timestamp) - .duration(1000) - .failure() - .errors( - instance - .error('[ResponseError] index_not_found_exception') - .timestamp(timestamp + 50) - ) .serialize() ); - const metricsets = range - .interval('30s') - .rate(1) - .flatMap((timestamp) => - instance - .appMetrics({ - 'system.memory.actual.free': 800, - 'system.memory.total': 1000, - 'system.cpu.total.norm.pct': 0.6, - 'system.process.cpu.total.norm.pct': 0.7, - }) - .timestamp(timestamp) - .serialize() - ); - return [...successfulTraceEvents, ...failedTraceEvents, ...metricsets]; - }); + return successfulTraceEvents.concat(failedTraceEvents, metricsets); + }; - return logger.perf('apm_events_to_es_output', () => - apmEventsToElasticsearchOutput({ - events: [ - ...events, - ...logger.perf('get_transaction_metrics', () => apm.getTransactionMetrics(events)), - ...logger.perf('get_span_destination_metrics', () => - apm.getSpanDestinationMetrics(events) - ), - ...logger.perf('get_breakdown_metrics', () => apm.getBreakdownMetrics(events)), - ], - writeTargets, - }) - ); - }); + return instances + .map((instance) => logger.perf('generating_apm_events', () => instanceSpans(instance))) + .reduce((p, c) => p.concat(c)); }, }; }; diff --git a/packages/elastic-apm-synthtrace/src/scripts/examples/02_kibana_stats.ts b/packages/elastic-apm-synthtrace/src/scripts/examples/02_kibana_stats.ts index 2ba3c4a29c52b..a19f0454e3447 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/examples/02_kibana_stats.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/examples/02_kibana_stats.ts @@ -7,14 +7,14 @@ */ import { stackMonitoring, timerange } from '../../index'; -import { eventsToElasticsearchOutput } from '../../lib/utils/to_elasticsearch_output'; import { Scenario } from '../scenario'; import { getCommonServices } from '../utils/get_common_services'; +import { RunOptions } from '../utils/parse_run_cli_flags'; -const scenario: Scenario = async ({ target, writeTarget, logLevel }) => { - const { logger } = getCommonServices({ target, logLevel }); +const scenario: Scenario = async (runOptions: RunOptions) => { + const { logger } = getCommonServices(runOptions); - if (!writeTarget) { + if (!runOptions.writeTarget) { throw new Error('Write target is not defined'); } @@ -26,20 +26,11 @@ const scenario: Scenario = async ({ target, writeTarget, logLevel }) => { return range .interval('30s') .rate(1) - .flatMap((timestamp) => { + .spans((timestamp) => { const events = logger.perf('generating_sm_events', () => { return kibanaStats.timestamp(timestamp).requests(10, 20).serialize(); }); - - return logger.perf('sm_events_to_es_output', () => { - const smEvents = eventsToElasticsearchOutput({ events, writeTarget }); - smEvents.forEach((event: any) => { - const ts = event._source['@timestamp']; - delete event._source['@timestamp']; - event._source.timestamp = ts; - }); - return smEvents; - }); + return events; }); }, }; diff --git a/packages/elastic-apm-synthtrace/src/scripts/examples/03_monitoring.ts b/packages/elastic-apm-synthtrace/src/scripts/examples/03_monitoring.ts index 53dcd820f5519..cd98cf6ca0636 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/examples/03_monitoring.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/examples/03_monitoring.ts @@ -9,32 +9,19 @@ // Run with: node ./src/scripts/run ./src/scripts/examples/03_monitoring.ts --target=http://elastic:changeme@localhost:9200 import { stackMonitoring, timerange } from '../../index'; -import { - ElasticsearchOutput, - eventsToElasticsearchOutput, -} from '../../lib/utils/to_elasticsearch_output'; import { Scenario } from '../scenario'; import { getCommonServices } from '../utils/get_common_services'; -import { StackMonitoringFields } from '../../lib/stack_monitoring/stack_monitoring_fields'; +import { RunOptions } from '../utils/parse_run_cli_flags'; -// TODO (mat): move this into a function like utils/apm_events_to_elasticsearch_output.ts -function smEventsToElasticsearchOutput( - events: StackMonitoringFields[], - writeTarget: string -): ElasticsearchOutput[] { - const smEvents = eventsToElasticsearchOutput({ events, writeTarget }); - smEvents.forEach((event: any) => { - const ts = event._source['@timestamp']; - delete event._source['@timestamp']; - event._source.timestamp = ts; - }); - return smEvents; -} - -const scenario: Scenario = async ({ target, logLevel }) => { - const { logger } = getCommonServices({ target, logLevel }); +const scenario: Scenario = async (runOptions: RunOptions) => { + const { logger } = getCommonServices(runOptions); return { + mapToIndex: (data) => { + return data.kibana_stats?.kibana?.name + ? '.monitoring-kibana-7-synthtrace' + : '.monitoring-es-7-synthtrace'; + }, generate: ({ from, to }) => { const cluster = stackMonitoring.cluster('test-cluster'); const clusterStats = cluster.stats(); @@ -44,24 +31,14 @@ const scenario: Scenario = async ({ target, logLevel }) => { return range .interval('10s') .rate(1) - .flatMap((timestamp) => { + .spans((timestamp) => { const clusterEvents = logger.perf('generating_es_events', () => { return clusterStats.timestamp(timestamp).indices(115).serialize(); }); - const clusterOutputs = smEventsToElasticsearchOutput( - clusterEvents, - '.monitoring-es-7-synthtrace' - ); - const kibanaEvents = logger.perf('generating_kb_events', () => { return kibanaStats.timestamp(timestamp).requests(10, 20).serialize(); }); - const kibanaOutputs = smEventsToElasticsearchOutput( - kibanaEvents, - '.monitoring-kibana-7-synthtrace' - ); - - return [...clusterOutputs, ...kibanaOutputs]; + return [...clusterEvents, ...kibanaEvents]; }); }, }; diff --git a/packages/elastic-apm-synthtrace/src/scripts/run.ts b/packages/elastic-apm-synthtrace/src/scripts/run.ts index 96bef3e958bdc..36b3974c12161 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/run.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/run.ts @@ -13,6 +13,8 @@ import { startHistoricalDataUpload } from './utils/start_historical_data_upload' import { startLiveDataUpload } from './utils/start_live_data_upload'; import { parseRunCliFlags } from './utils/parse_run_cli_flags'; import { getCommonServices } from './utils/get_common_services'; +import { ApmSynthtraceKibanaClient } from '../lib/apm/client/apm_synthtrace_kibana_client'; +import { ApmSynthtraceEsClient } from '../lib/apm/client/apm_synthtrace_es_client'; function options(y: Argv) { return y @@ -22,9 +24,23 @@ function options(y: Argv) { string: true, }) .option('target', { - describe: 'Elasticsearch target, including username/password', + describe: 'Elasticsearch target', + string: true, + }) + .option('cloudId', { + describe: + 'Provide connection information and will force APM on the cloud to migrate to run as a Fleet integration', + string: true, + }) + .option('username', { + describe: 'Basic authentication username', + string: true, demandOption: true, + }) + .option('password', { + describe: 'Basic authentication password', string: true, + demandOption: true, }) .option('from', { description: 'The start of the time window', @@ -36,6 +52,20 @@ function options(y: Argv) { description: 'Generate and index data continuously', boolean: true, }) + .option('--dryRun', { + description: 'Enumerates the stream without sending events to Elasticsearch ', + boolean: true, + }) + .option('maxDocs', { + description: + 'The maximum number of documents we are allowed to generate, should be multiple of 10.000', + number: true, + }) + .option('numShards', { + description: + 'Updates the component templates to update the number of primary shards, requires cloudId to be provided', + number: true, + }) .option('clean', { describe: 'Clean APM indices before indexing new data', default: false, @@ -75,7 +105,9 @@ function options(y: Argv) { return arg as Record | undefined; }, }) - .conflicts('to', 'live'); + .conflicts('to', 'live') + .conflicts('maxDocs', 'live') + .conflicts('target', 'cloudId'); } export type RunCliFlags = ReturnType['argv']; @@ -84,38 +116,57 @@ yargs(process.argv.slice(2)) .command('*', 'Generate data and index into Elasticsearch', options, async (argv) => { const runOptions = parseRunCliFlags(argv); - const { logger } = getCommonServices(runOptions); + const { logger, client } = getCommonServices(runOptions); - const to = datemath.parse(String(argv.to ?? 'now'))!.valueOf(); - const from = argv.from + const toMs = datemath.parse(String(argv.to ?? 'now'))!.valueOf(); + const to = new Date(toMs); + const defaultTimeRange = !runOptions.maxDocs ? '15m' : '52w'; + const fromMs = argv.from ? datemath.parse(String(argv.from))!.valueOf() - : to - intervalToMs('15m'); + : toMs - intervalToMs(defaultTimeRange); + const from = new Date(fromMs); const live = argv.live; + const forceDataStreams = !!runOptions.cloudId; + const esClient = new ApmSynthtraceEsClient(client, logger, forceDataStreams); + if (runOptions.dryRun) { + await startHistoricalDataUpload(esClient, logger, runOptions, from, to); + return; + } + if (runOptions.cloudId) { + const kibanaClient = new ApmSynthtraceKibanaClient(logger); + await kibanaClient.migrateCloudToManagedApm( + runOptions.cloudId, + runOptions.username, + runOptions.password + ); + } + + if (runOptions.cloudId && runOptions.numShards && runOptions.numShards > 0) { + await esClient.updateComponentTemplates(runOptions.numShards); + } + + if (argv.clean) { + await esClient.clean(); + } + logger.info( `Starting data generation\n: ${JSON.stringify( { ...runOptions, - from: new Date(from).toISOString(), - to: new Date(to).toISOString(), + from: from.toISOString(), + to: to.toISOString(), }, null, 2 )}` ); - startHistoricalDataUpload({ - ...runOptions, - from, - to, - }); + await startHistoricalDataUpload(esClient, logger, runOptions, from, to); if (live) { - startLiveDataUpload({ - ...runOptions, - start: to, - }); + await startLiveDataUpload(esClient, logger, runOptions, to); } }) .parse(); diff --git a/packages/elastic-apm-synthtrace/src/scripts/scenario.ts b/packages/elastic-apm-synthtrace/src/scripts/scenario.ts index c134c08cd8354..089babab04413 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/scenario.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/scenario.ts @@ -6,8 +6,11 @@ * Side Public License, v 1. */ -import { ElasticsearchOutput } from '../lib/utils/to_elasticsearch_output'; import { RunOptions } from './utils/parse_run_cli_flags'; +import { SpanIterable } from '../lib/span_iterable'; -type Generate = (range: { from: number; to: number }) => ElasticsearchOutput[]; -export type Scenario = (options: RunOptions) => Promise<{ generate: Generate }>; +type Generate = (range: { from: Date; to: Date }) => SpanIterable; +export type Scenario = (options: RunOptions) => Promise<{ + generate: Generate; + mapToIndex?: (data: Record) => string; +}>; diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/get_common_services.ts b/packages/elastic-apm-synthtrace/src/scripts/utils/get_common_services.ts index 0dee6dbc951eb..88e5c75e39995 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/get_common_services.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/utils/get_common_services.ts @@ -6,13 +6,21 @@ * Side Public License, v 1. */ -import { Client } from '@elastic/elasticsearch'; -import { createLogger, LogLevel } from '../../lib/utils/create_logger'; +import { Client, ClientOptions } from '@elastic/elasticsearch'; +import { createLogger } from '../../lib/utils/create_logger'; +import { RunOptions } from './parse_run_cli_flags'; -export function getCommonServices({ target, logLevel }: { target: string; logLevel: LogLevel }) { - const client = new Client({ - node: target, - }); +export function getCommonServices({ target, cloudId, username, password, logLevel }: RunOptions) { + if (!target && !cloudId) { + throw Error('target or cloudId needs to be specified'); + } + const options: ClientOptions = !!target ? { node: target } : { cloud: { id: cloudId! } }; + options.auth = { + username, + password, + }; + + const client = new Client(options); const logger = createLogger(logLevel); diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/parse_run_cli_flags.ts b/packages/elastic-apm-synthtrace/src/scripts/utils/parse_run_cli_flags.ts index 47359bd07aa8a..5a8b6f96c6316 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/parse_run_cli_flags.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/utils/parse_run_cli_flags.ts @@ -49,12 +49,18 @@ export function parseRunCliFlags(flags: RunCliFlags) { return { ...pick( flags, + 'maxDocs', 'target', + 'cloudId', + 'username', + 'password', 'workers', 'clientWorkers', 'batchSize', 'writeTarget', - 'scenarioOpts' + 'numShards', + 'scenarioOpts', + 'dryRun' ), intervalInMs, bucketSizeInMs, diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/start_historical_data_upload.ts b/packages/elastic-apm-synthtrace/src/scripts/utils/start_historical_data_upload.ts index ee462085ef79c..415292a85e0e0 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/start_historical_data_upload.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/utils/start_historical_data_upload.ts @@ -5,101 +5,55 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import pLimit from 'p-limit'; -import Path from 'path'; -import { Worker } from 'worker_threads'; -import { getCommonServices } from './get_common_services'; import { RunOptions } from './parse_run_cli_flags'; -import { WorkerData } from './upload_next_batch'; - -export async function startHistoricalDataUpload({ - from, - to, - intervalInMs, - bucketSizeInMs, - workers, - clientWorkers, - batchSize, - logLevel, - target, - file, - writeTarget, - scenarioOpts, -}: RunOptions & { from: number; to: number }) { - let requestedUntil: number = from; - - const { logger } = getCommonServices({ target, logLevel }); - - function processNextBatch() { - const bucketFrom = requestedUntil; - const bucketTo = Math.min(to, bucketFrom + bucketSizeInMs); - - if (bucketFrom === bucketTo) { - return; - } - - requestedUntil = bucketTo; - - logger.info( - `Starting worker for ${new Date(bucketFrom).toISOString()} to ${new Date( - bucketTo - ).toISOString()}` - ); - - const workerData: WorkerData = { - bucketFrom, - bucketTo, - file, - logLevel, - batchSize, - bucketSizeInMs, - clientWorkers, - intervalInMs, - target, - workers, - writeTarget, - scenarioOpts, - }; - - const worker = new Worker(Path.join(__dirname, './upload_next_batch.js'), { - workerData, - }); - - logger.perf('created_worker', () => { - return new Promise((resolve, reject) => { - worker.on('online', () => { - resolve(); - }); - }); - }); - - logger.perf('completed_worker', () => { - return new Promise((resolve, reject) => { - worker.on('exit', () => { - resolve(); - }); - }); - }); - - return new Promise((resolve, reject) => { - worker.on('error', (err) => { - reject(err); - }); - - worker.on('exit', (code) => { - if (code !== 0) { - reject(new Error(`Worker stopped: exit code ${code}`)); - return; - } - logger.debug('Worker completed'); - resolve(); - }); +import { getScenario } from './get_scenario'; +import { ApmSynthtraceEsClient } from '../../lib/apm'; +import { Logger } from '../../lib/utils/create_logger'; +import { StreamProcessor } from '../../lib/stream_processor'; + +export async function startHistoricalDataUpload( + esClient: ApmSynthtraceEsClient, + logger: Logger, + runOptions: RunOptions, + from: Date, + to: Date +) { + const file = runOptions.file; + const scenario = await logger.perf('get_scenario', () => getScenario({ file, logger })); + + const { generate, mapToIndex } = await scenario(runOptions); + + // if we want to generate a maximum number of documents reverse generation to descend. + [from, to] = runOptions.maxDocs ? [to, from] : [from, to]; + + logger.info(`Generating data from ${from} to ${to}`); + + const events = logger.perf('generate_scenario', () => generate({ from, to })); + + if (runOptions.dryRun) { + const maxDocs = runOptions.maxDocs; + const stream = new StreamProcessor({ + processors: StreamProcessor.apmProcessors, + maxSourceEvents: maxDocs, + logger, + }).streamToDocument(StreamProcessor.toDocument, events); + logger.perf('enumerate_scenario', () => { + // @ts-ignore + // We just want to enumerate + let yielded = 0; + for (const _ of stream) { + yielded++; + } }); + return; } - const numBatches = Math.ceil((to - from) / bucketSizeInMs); - - const limiter = pLimit(workers); - - return Promise.all(new Array(numBatches).fill(undefined).map((_) => limiter(processNextBatch))); + const clientWorkers = runOptions.clientWorkers; + await logger.perf('index_scenario', () => + esClient.index(events, { + concurrency: clientWorkers, + maxDocs: runOptions.maxDocs, + mapToIndex, + }) + ); } diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/start_live_data_upload.ts b/packages/elastic-apm-synthtrace/src/scripts/utils/start_live_data_upload.ts index ab4eee4f255b9..b3e4776498788 100644 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/start_live_data_upload.ts +++ b/packages/elastic-apm-synthtrace/src/scripts/utils/start_live_data_upload.ts @@ -8,49 +8,36 @@ import { partition } from 'lodash'; import { getScenario } from './get_scenario'; -import { uploadEvents } from './upload_events'; import { RunOptions } from './parse_run_cli_flags'; -import { getCommonServices } from './get_common_services'; -import { ElasticsearchOutput } from '../../lib/utils/to_elasticsearch_output'; +import { ApmFields } from '../../lib/apm/apm_fields'; +import { ApmSynthtraceEsClient } from '../../lib/apm'; +import { Logger } from '../../lib/utils/create_logger'; +import { SpanArrayIterable } from '../../lib/span_iterable'; -export async function startLiveDataUpload({ - file, - start, - bucketSizeInMs, - intervalInMs, - clientWorkers, - batchSize, - target, - logLevel, - workers, - writeTarget, - scenarioOpts, -}: RunOptions & { start: number }) { - let queuedEvents: ElasticsearchOutput[] = []; - let requestedUntil: number = start; - - const { logger, client } = getCommonServices({ target, logLevel }); +export async function startLiveDataUpload( + esClient: ApmSynthtraceEsClient, + logger: Logger, + runOptions: RunOptions, + start: Date +) { + const file = runOptions.file; const scenario = await getScenario({ file, logger }); - const { generate } = await scenario({ - batchSize, - bucketSizeInMs, - clientWorkers, - file, - intervalInMs, - logLevel, - target, - workers, - writeTarget, - scenarioOpts, - }); + const { generate, mapToIndex } = await scenario(runOptions); + + let queuedEvents: ApmFields[] = []; + let requestedUntil: Date = start; - function uploadNextBatch() { - const end = new Date().getTime(); + async function uploadNextBatch() { + const end = new Date(); if (end > requestedUntil) { const bucketFrom = requestedUntil; - const bucketTo = requestedUntil + bucketSizeInMs; - const nextEvents = generate({ from: bucketFrom, to: bucketTo }); + const bucketTo = new Date(requestedUntil.getTime() + runOptions.bucketSizeInMs); + // TODO this materializes into an array, assumption is that the live buffer will fit in memory + const nextEvents = logger.perf('execute_scenario', () => + generate({ from: bucketFrom, to: bucketTo }).toArray() + ); + logger.debug( `Requesting ${new Date(bucketFrom).toISOString()} to ${new Date( bucketTo @@ -62,23 +49,27 @@ export async function startLiveDataUpload({ const [eventsToUpload, eventsToRemainInQueue] = partition( queuedEvents, - (event) => event.timestamp <= end + (event) => event['@timestamp'] !== undefined && event['@timestamp'] <= end.getTime() ); logger.info(`Uploading until ${new Date(end).toISOString()}, events: ${eventsToUpload.length}`); queuedEvents = eventsToRemainInQueue; - uploadEvents({ - events: eventsToUpload, - clientWorkers, - batchSize, - logger, - client, - }); + await logger.perf('index_live_scenario', () => + esClient.index(new SpanArrayIterable(eventsToUpload), { + concurrency: runOptions.clientWorkers, + maxDocs: runOptions.maxDocs, + mapToIndex, + }) + ); } - setInterval(uploadNextBatch, intervalInMs); - - uploadNextBatch(); + do { + await uploadNextBatch(); + await delay(runOptions.intervalInMs); + } while (true); +} +async function delay(ms: number) { + return await new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/upload_events.ts b/packages/elastic-apm-synthtrace/src/scripts/utils/upload_events.ts deleted file mode 100644 index d68a1b88132bb..0000000000000 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/upload_events.ts +++ /dev/null @@ -1,66 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -import { Client } from '@elastic/elasticsearch'; -import { chunk } from 'lodash'; -import pLimit from 'p-limit'; -import { inspect } from 'util'; -import { ElasticsearchOutput } from '../../lib/utils/to_elasticsearch_output'; -import { Logger } from '../../lib/utils/create_logger'; - -export function uploadEvents({ - events, - client, - clientWorkers, - batchSize, - logger, -}: { - events: ElasticsearchOutput[]; - client: Client; - clientWorkers: number; - batchSize: number; - logger: Logger; -}) { - const fn = pLimit(clientWorkers); - - const batches = chunk(events, batchSize); - - if (!batches.length) { - return; - } - - logger.debug(`Uploading ${events.length} in ${batches.length} batches`); - - const time = new Date().getTime(); - - return Promise.all( - batches.map((batch) => - fn(() => { - return logger.perf('bulk_upload', () => - client.bulk({ - refresh: false, - body: batch.flatMap((doc) => { - return [{ index: { _index: doc._index } }, doc._source]; - }), - }) - ); - }) - ) - ).then((results) => { - const errors = results - .flatMap((result) => result.items) - .filter((item) => !!item.index?.error) - .map((item) => item.index?.error); - - if (errors.length) { - logger.error(inspect(errors.slice(0, 10), { depth: null })); - throw new Error('Failed to upload some items'); - } - - logger.debug(`Uploaded ${events.length} in ${new Date().getTime() - time}ms`); - }); -} diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/upload_next_batch.js b/packages/elastic-apm-synthtrace/src/scripts/utils/upload_next_batch.js deleted file mode 100644 index 86c7b67c24ff7..0000000000000 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/upload_next_batch.js +++ /dev/null @@ -1,15 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/* eslint-disable @typescript-eslint/no-var-requires*/ -require('@babel/register')({ - extensions: ['.ts', '.js'], - presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'], -}); - -require('./upload_next_batch.ts'); diff --git a/packages/elastic-apm-synthtrace/src/scripts/utils/upload_next_batch.ts b/packages/elastic-apm-synthtrace/src/scripts/utils/upload_next_batch.ts deleted file mode 100644 index 973cbc2266cbe..0000000000000 --- a/packages/elastic-apm-synthtrace/src/scripts/utils/upload_next_batch.ts +++ /dev/null @@ -1,95 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -// add this to workerExample.js file. -import { Client } from '@elastic/elasticsearch'; -import { workerData } from 'worker_threads'; -import { getScenario } from './get_scenario'; -import { createLogger, LogLevel } from '../../lib/utils/create_logger'; -import { uploadEvents } from './upload_events'; - -export interface WorkerData { - bucketFrom: number; - bucketTo: number; - file: string; - scenarioOpts: Record | undefined; - logLevel: LogLevel; - clientWorkers: number; - batchSize: number; - intervalInMs: number; - bucketSizeInMs: number; - target: string; - workers: number; - writeTarget?: string; -} - -const { - bucketFrom, - bucketTo, - file, - logLevel, - clientWorkers, - batchSize, - intervalInMs, - bucketSizeInMs, - workers, - target, - writeTarget, - scenarioOpts, -} = workerData as WorkerData; - -async function uploadNextBatch() { - if (bucketFrom === bucketTo) { - return; - } - - const logger = createLogger(logLevel); - const client = new Client({ - node: target, - }); - - const scenario = await logger.perf('get_scenario', () => getScenario({ file, logger })); - - const { generate } = await scenario({ - intervalInMs, - bucketSizeInMs, - logLevel, - file, - clientWorkers, - batchSize, - target, - workers, - writeTarget, - scenarioOpts, - }); - - const events = logger.perf('execute_scenario', () => - generate({ from: bucketFrom, to: bucketTo }) - ); - - return uploadEvents({ - events, - client, - clientWorkers, - batchSize, - logger, - }); -} - -uploadNextBatch() - .then(() => { - process.exit(0); - }) - .catch((error) => { - // eslint-disable-next-line - console.log(error); - // make sure error shows up in console before process is killed - setTimeout(() => { - process.exit(1); - }, 100); - }); diff --git a/packages/elastic-apm-synthtrace/src/test/apm_events_to_elasticsearch_output.test.ts b/packages/elastic-apm-synthtrace/src/test/apm_events_to_elasticsearch_output.test.ts index b8d0302558925..defdcc73cf6cb 100644 --- a/packages/elastic-apm-synthtrace/src/test/apm_events_to_elasticsearch_output.test.ts +++ b/packages/elastic-apm-synthtrace/src/test/apm_events_to_elasticsearch_output.test.ts @@ -6,15 +6,8 @@ * Side Public License, v 1. */ -import { apmEventsToElasticsearchOutput } from '../lib/apm/utils/apm_events_to_elasticsearch_output'; import { ApmFields } from '../lib/apm/apm_fields'; - -const writeTargets = { - transaction: 'apm-8.0.0-transaction', - span: 'apm-8.0.0-span', - metric: 'apm-8.0.0-metric', - error: 'apm-8.0.0-error', -}; +import { StreamProcessor } from '../lib/stream_processor'; describe('output apm events to elasticsearch', () => { let event: ApmFields; @@ -29,32 +22,31 @@ describe('output apm events to elasticsearch', () => { }); it('properly formats @timestamp', () => { - const doc = apmEventsToElasticsearchOutput({ events: [event], writeTargets })[0] as any; - - expect(doc._source['@timestamp']).toEqual('2020-12-31T23:00:00.000Z'); + const doc = StreamProcessor.toDocument(event); + expect(doc['@timestamp']).toEqual('2020-12-31T23:00:00.000Z'); }); it('formats a nested object', () => { - const doc = apmEventsToElasticsearchOutput({ events: [event], writeTargets })[0] as any; + const doc = StreamProcessor.toDocument(event); - expect(doc._source.processor).toEqual({ + expect(doc.processor).toEqual({ event: 'transaction', name: 'transaction', }); }); it('formats all fields consistently', () => { - const doc = apmEventsToElasticsearchOutput({ events: [event], writeTargets })[0] as any; + const doc = StreamProcessor.toDocument(event); - expect(doc._source).toMatchInlineSnapshot(` + expect(doc).toMatchInlineSnapshot(` Object { "@timestamp": "2020-12-31T23:00:00.000Z", "ecs": Object { "version": "1.4", }, "observer": Object { - "version": "7.16.0", - "version_major": 7, + "version": "8.0.0", + "version_major": 8, }, "processor": Object { "event": "transaction", diff --git a/packages/elastic-apm-synthtrace/src/test/scenarios/01_simple_trace.test.ts b/packages/elastic-apm-synthtrace/src/test/scenarios/01_simple_trace.test.ts index a78f1ec987bcf..ce42dfc2a8e6c 100644 --- a/packages/elastic-apm-synthtrace/src/test/scenarios/01_simple_trace.test.ts +++ b/packages/elastic-apm-synthtrace/src/test/scenarios/01_simple_trace.test.ts @@ -17,14 +17,14 @@ describe('simple trace', () => { const javaInstance = javaService.instance('instance-1'); const range = timerange( - new Date('2021-01-01T00:00:00.000Z').getTime(), - new Date('2021-01-01T00:15:00.000Z').getTime() + new Date('2021-01-01T00:00:00.000Z'), + new Date('2021-01-01T00:15:00.000Z') ); events = range .interval('1m') .rate(1) - .flatMap((timestamp) => + .spans((timestamp) => javaInstance .transaction('GET /api/product/list') .duration(1000) @@ -38,7 +38,8 @@ describe('simple trace', () => { .timestamp(timestamp + 50) ) .serialize() - ); + ) + .toArray(); }); it('generates the same data every time', () => { diff --git a/packages/elastic-apm-synthtrace/src/test/scenarios/02_transaction_metrics.test.ts b/packages/elastic-apm-synthtrace/src/test/scenarios/02_transaction_metrics.test.ts index d074bcbf6c1fd..f5a99b3f389d6 100644 --- a/packages/elastic-apm-synthtrace/src/test/scenarios/02_transaction_metrics.test.ts +++ b/packages/elastic-apm-synthtrace/src/test/scenarios/02_transaction_metrics.test.ts @@ -8,7 +8,8 @@ import { apm } from '../../lib/apm'; import { timerange } from '../../lib/timerange'; -import { getTransactionMetrics } from '../../lib/apm/utils/get_transaction_metrics'; +import { getTransactionMetrics } from '../../lib/apm/processors/get_transaction_metrics'; +import { StreamProcessor } from '../../lib/stream_processor'; describe('transaction metrics', () => { let events: Array>; @@ -18,36 +19,29 @@ describe('transaction metrics', () => { const javaInstance = javaService.instance('instance-1'); const range = timerange( - new Date('2021-01-01T00:00:00.000Z').getTime(), - new Date('2021-01-01T00:15:00.000Z').getTime() + new Date('2021-01-01T00:00:00.000Z'), + new Date('2021-01-01T00:15:00.000Z') ); - events = getTransactionMetrics( - range - .interval('1m') - .rate(25) - .flatMap((timestamp) => - javaInstance - .transaction('GET /api/product/list') - .duration(1000) - .success() - .timestamp(timestamp) - .serialize() - ) - .concat( - range - .interval('1m') - .rate(50) - .flatMap((timestamp) => - javaInstance - .transaction('GET /api/product/list') - .duration(1000) - .failure() - .timestamp(timestamp) - .serialize() - ) - ) - ); + const span = (timestamp: number) => + javaInstance.transaction('GET /api/product/list').duration(1000).timestamp(timestamp); + + const processor = new StreamProcessor({ + processors: [getTransactionMetrics], + flushInterval: '15m', + }); + events = processor + .streamToArray( + range + .interval('1m') + .rate(25) + .spans((timestamp) => span(timestamp).success().serialize()), + range + .interval('1m') + .rate(50) + .spans((timestamp) => span(timestamp).failure().serialize()) + ) + .filter((fields) => fields['metricset.name'] === 'transaction'); }); it('generates the right amount of transaction metrics', () => { diff --git a/packages/elastic-apm-synthtrace/src/test/scenarios/03_span_destination_metrics.test.ts b/packages/elastic-apm-synthtrace/src/test/scenarios/03_span_destination_metrics.test.ts index fe4734c65739c..b81dea4522064 100644 --- a/packages/elastic-apm-synthtrace/src/test/scenarios/03_span_destination_metrics.test.ts +++ b/packages/elastic-apm-synthtrace/src/test/scenarios/03_span_destination_metrics.test.ts @@ -8,7 +8,8 @@ import { apm } from '../../lib/apm'; import { timerange } from '../../lib/timerange'; -import { getSpanDestinationMetrics } from '../../lib/apm/utils/get_span_destination_metrics'; +import { getSpanDestinationMetrics } from '../../lib/apm/processors/get_span_destination_metrics'; +import { StreamProcessor } from '../../lib/stream_processor'; describe('span destination metrics', () => { let events: Array>; @@ -18,57 +19,57 @@ describe('span destination metrics', () => { const javaInstance = javaService.instance('instance-1'); const range = timerange( - new Date('2021-01-01T00:00:00.000Z').getTime(), - new Date('2021-01-01T00:15:00.000Z').getTime() - ); - - events = getSpanDestinationMetrics( - range - .interval('1m') - .rate(25) - .flatMap((timestamp) => - javaInstance - .transaction('GET /api/product/list') - .duration(1000) - .success() - .timestamp(timestamp) - .children( - javaInstance - .span('GET apm-*/_search', 'db', 'elasticsearch') - .timestamp(timestamp) - .duration(1000) - .destination('elasticsearch') - .success() - ) - .serialize() - ) - .concat( - range - .interval('1m') - .rate(50) - .flatMap((timestamp) => - javaInstance - .transaction('GET /api/product/list') - .duration(1000) - .failure() - .timestamp(timestamp) - .children( - javaInstance - .span('GET apm-*/_search', 'db', 'elasticsearch') - .timestamp(timestamp) - .duration(1000) - .destination('elasticsearch') - .failure(), - javaInstance - .span('custom_operation', 'app') - .timestamp(timestamp) - .duration(500) - .success() - ) - .serialize() - ) - ) + new Date('2021-01-01T00:00:00.000Z'), + new Date('2021-01-01T00:15:00.000Z') ); + const processor = new StreamProcessor({ processors: [getSpanDestinationMetrics] }); + events = processor + .streamToArray( + range + .interval('1m') + .rate(25) + .spans((timestamp) => + javaInstance + .transaction('GET /api/product/list') + .duration(1000) + .success() + .timestamp(timestamp) + .children( + javaInstance + .span('GET apm-*/_search', 'db', 'elasticsearch') + .timestamp(timestamp) + .duration(1000) + .destination('elasticsearch') + .success() + ) + .serialize() + ), + range + .interval('1m') + .rate(50) + .spans((timestamp) => + javaInstance + .transaction('GET /api/product/list') + .duration(1000) + .failure() + .timestamp(timestamp) + .children( + javaInstance + .span('GET apm-*/_search', 'db', 'elasticsearch') + .timestamp(timestamp) + .duration(1000) + .destination('elasticsearch') + .failure(), + javaInstance + .span('custom_operation', 'app') + .timestamp(timestamp) + .duration(500) + .success() + ) + .serialize() + ) + ) + .filter((fields) => fields['metricset.name'] === 'span_destination'); }); it('generates the right amount of span metrics', () => { diff --git a/packages/elastic-apm-synthtrace/src/test/scenarios/04_breakdown_metrics.test.ts b/packages/elastic-apm-synthtrace/src/test/scenarios/04_breakdown_metrics.test.ts index 817f0aad9f5e4..45f956834d763 100644 --- a/packages/elastic-apm-synthtrace/src/test/scenarios/04_breakdown_metrics.test.ts +++ b/packages/elastic-apm-synthtrace/src/test/scenarios/04_breakdown_metrics.test.ts @@ -8,8 +8,9 @@ import { sumBy } from 'lodash'; import { apm } from '../../lib/apm'; import { timerange } from '../../lib/timerange'; -import { getBreakdownMetrics } from '../../lib/apm/utils/get_breakdown_metrics'; +import { getBreakdownMetrics } from '../../lib/apm/processors/get_breakdown_metrics'; import { ApmFields } from '../../lib/apm/apm_fields'; +import { StreamProcessor } from '../../lib/stream_processor'; describe('breakdown metrics', () => { let events: ApmFields[]; @@ -24,51 +25,58 @@ describe('breakdown metrics', () => { const javaService = apm.service('opbeans-java', 'production', 'java'); const javaInstance = javaService.instance('instance-1'); - const start = new Date('2021-01-01T00:00:00.000Z').getTime(); - - const range = timerange(start, start + INTERVALS * 30 * 1000); - - events = getBreakdownMetrics([ - ...range - .interval('30s') - .rate(LIST_RATE) - .flatMap((timestamp) => - javaInstance - .transaction('GET /api/product/list') - .timestamp(timestamp) - .duration(1000) - .children( - javaInstance - .span('GET apm-*/_search', 'db', 'elasticsearch') - .timestamp(timestamp + 150) - .duration(500), - javaInstance.span('GET foo', 'db', 'redis').timestamp(timestamp).duration(100) - ) - .serialize() - ), - ...range - .interval('30s') - .rate(ID_RATE) - .flatMap((timestamp) => - javaInstance - .transaction('GET /api/product/:id') - .timestamp(timestamp) - .duration(1000) - .children( - javaInstance - .span('GET apm-*/_search', 'db', 'elasticsearch') - .duration(500) - .timestamp(timestamp + 100) - .children( - javaInstance - .span('bar', 'external', 'http') - .timestamp(timestamp + 200) - .duration(100) - ) - ) - .serialize() - ), - ]).filter((event) => event['processor.event'] === 'metric'); + const start = new Date('2021-01-01T00:00:00.000Z'); + + const range = timerange(start, new Date(start.getTime() + INTERVALS * 30 * 1000)); + + const listSpans = range + .interval('30s') + .rate(LIST_RATE) + .spans((timestamp) => + javaInstance + .transaction('GET /api/product/list') + .timestamp(timestamp) + .duration(1000) + .children( + javaInstance + .span('GET apm-*/_search', 'db', 'elasticsearch') + .timestamp(timestamp + 150) + .duration(500), + javaInstance.span('GET foo', 'db', 'redis').timestamp(timestamp).duration(100) + ) + .serialize() + ); + + const productPageSpans = range + .interval('30s') + .rate(ID_RATE) + .spans((timestamp) => + javaInstance + .transaction('GET /api/product/:id') + .timestamp(timestamp) + .duration(1000) + .children( + javaInstance + .span('GET apm-*/_search', 'db', 'elasticsearch') + .duration(500) + .timestamp(timestamp + 100) + .children( + javaInstance + .span('bar', 'external', 'http') + .timestamp(timestamp + 200) + .duration(100) + ) + ) + .serialize() + ); + + const processor = new StreamProcessor({ + processors: [getBreakdownMetrics], + flushInterval: '15m', + }); + events = processor + .streamToArray(listSpans, productPageSpans) + .filter((event) => event['processor.event'] === 'metric'); }); it('generates the right amount of breakdown metrics', () => { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/fixtures/synthtrace/opbeans.ts b/x-pack/plugins/apm/ftr_e2e/cypress/fixtures/synthtrace/opbeans.ts index b51874f951c0e..6ef38a9f63baa 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/fixtures/synthtrace/opbeans.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/fixtures/synthtrace/opbeans.ts @@ -24,47 +24,43 @@ export function opbeans({ from, to }: { from: number; to: number }) { apm.getChromeUserAgentDefaults() ); - return [ - ...range - .interval('1s') - .rate(1) - .flatMap((timestamp) => [ - ...opbeansJava - .transaction('GET /api/product') - .timestamp(timestamp) - .duration(1000) - .success() - .errors( - opbeansJava - .error('[MockError] Foo', `Exception`) - .timestamp(timestamp) - ) - .children( - opbeansJava - .span('SELECT * FROM product', 'db', 'postgresql') - .timestamp(timestamp) - .duration(50) - .success() - .destination('postgresql') - ) - .serialize(), - ...opbeansNode - .transaction('GET /api/product/:id') - .timestamp(timestamp) - .duration(500) - .success() - .serialize(), - ...opbeansNode - .transaction('Worker job', 'Worker') - .timestamp(timestamp) - .duration(1000) - .success() - .serialize(), - ...opbeansRum - .transaction('/') - .timestamp(timestamp) - .duration(1000) - .serialize(), - ]), - ]; + return range + .interval('1s') + .rate(1) + .spans((timestamp) => [ + ...opbeansJava + .transaction('GET /api/product') + .timestamp(timestamp) + .duration(1000) + .success() + .errors( + opbeansJava.error('[MockError] Foo', `Exception`).timestamp(timestamp) + ) + .children( + opbeansJava + .span('SELECT * FROM product', 'db', 'postgresql') + .timestamp(timestamp) + .duration(50) + .success() + .destination('postgresql') + ) + .serialize(), + ...opbeansNode + .transaction('GET /api/product/:id') + .timestamp(timestamp) + .duration(500) + .success() + .serialize(), + ...opbeansNode + .transaction('Worker job', 'Worker') + .timestamp(timestamp) + .duration(1000) + .success() + .serialize(), + ...opbeansRum + .transaction('/') + .timestamp(timestamp) + .duration(1000) + .serialize(), + ]); } diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/generate_data.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/generate_data.ts index 7215d2f435e1a..c2c02d8997153 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/generate_data.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/generate_data.ts @@ -18,28 +18,26 @@ export function generateData({ from, to }: { from: number; to: number }) { .service('opbeans-node', 'production', 'nodejs') .instance('opbeans-node-prod-1'); - return [ - ...range - .interval('2m') - .rate(1) - .flatMap((timestamp, index) => [ - ...opbeansJava - .transaction('GET /apple 🍎 ') - .timestamp(timestamp) - .duration(1000) - .success() - .errors( - opbeansJava - .error(`Error ${index}`, `exception ${index}`) - .timestamp(timestamp) - ) - .serialize(), - ...opbeansNode - .transaction('GET /banana 🍌') - .timestamp(timestamp) - .duration(500) - .success() - .serialize(), - ]), - ]; + return range + .interval('2m') + .rate(1) + .spans((timestamp, index) => [ + ...opbeansJava + .transaction('GET /apple 🍎 ') + .timestamp(timestamp) + .duration(1000) + .success() + .errors( + opbeansJava + .error(`Error ${index}`, `exception ${index}`) + .timestamp(timestamp) + ) + .serialize(), + ...opbeansNode + .transaction('GET /banana 🍌') + .timestamp(timestamp) + .duration(500) + .success() + .serialize(), + ]); } diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/header_filters/generate_data.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/header_filters/generate_data.ts index d4a2cdf103027..1e1e6db5ccaf5 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/header_filters/generate_data.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/header_filters/generate_data.ts @@ -26,23 +26,21 @@ export function generateData({ .service('opbeans-node', 'production', 'nodejs') .instance('opbeans-node-prod-1'); - return [ - ...range - .interval('2m') - .rate(1) - .flatMap((timestamp, index) => [ - ...service1 - .transaction('GET /apple 🍎 ') - .timestamp(timestamp) - .duration(1000) - .success() - .serialize(), - ...opbeansNode - .transaction('GET /banana 🍌') - .timestamp(timestamp) - .duration(500) - .success() - .serialize(), - ]), - ]; + return range + .interval('2m') + .rate(1) + .spans((timestamp, index) => [ + ...service1 + .transaction('GET /apple 🍎 ') + .timestamp(timestamp) + .duration(1000) + .success() + .serialize(), + ...opbeansNode + .transaction('GET /banana 🍌') + .timestamp(timestamp) + .duration(500) + .success() + .serialize(), + ]); } diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/aws_lambda/generate_data.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/aws_lambda/generate_data.ts index 2dba10e8e517e..fffa3e98a2c66 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/aws_lambda/generate_data.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/aws_lambda/generate_data.ts @@ -24,8 +24,8 @@ export function generateData({ start, end }: { start: number; end: number }) { const traceEvents = timerange(start, end) .interval('1m') .rate(rate) - .flatMap((timestamp) => [ - ...instance + .spans((timestamp) => + instance .transaction(transaction.name) .defaults({ 'service.runtime.name': 'AWS_Lambda_python3.8', @@ -34,8 +34,8 @@ export function generateData({ start, end }: { start: number; end: number }) { .timestamp(timestamp) .duration(transaction.duration) .success() - .serialize(), - ]); + .serialize() + ); return traceEvents; } diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/plugins/index.ts b/x-pack/plugins/apm/ftr_e2e/cypress/plugins/index.ts index 0c924e70a1fdf..6b6aff63976d5 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/plugins/index.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/plugins/index.ts @@ -4,7 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { apm, createLogger, LogLevel } from '@elastic/apm-synthtrace'; +import { + apm, + createLogger, + LogLevel, + SpanIterable, +} from '@elastic/apm-synthtrace'; import { createEsClientForTesting } from '@kbn/test'; // *********************************************************** @@ -33,13 +38,15 @@ const plugin: Cypress.PluginConfig = (on, config) => { isCloud: !!config.env.TEST_CLOUD, }); + const forceDataStreams = false; const synthtraceEsClient = new apm.ApmSynthtraceEsClient( client, - createLogger(LogLevel.info) + createLogger(LogLevel.info), + forceDataStreams ); on('task', { - 'synthtrace:index': async (events) => { + 'synthtrace:index': async (events: SpanIterable) => { await synthtraceEsClient.index(events); return null; }, diff --git a/x-pack/plugins/apm/ftr_e2e/synthtrace.ts b/x-pack/plugins/apm/ftr_e2e/synthtrace.ts index 3c818b65200b6..2409dded17780 100644 --- a/x-pack/plugins/apm/ftr_e2e/synthtrace.ts +++ b/x-pack/plugins/apm/ftr_e2e/synthtrace.ts @@ -4,9 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { SpanIterable } from '@elastic/apm-synthtrace'; export const synthtrace = { - index: (events: any[]) => + index: (events: SpanIterable) => new Promise((resolve) => { cy.task('synthtrace:index', events).then(resolve); }), diff --git a/x-pack/test/apm_api_integration/common/synthtrace_es_client_service.ts b/x-pack/test/apm_api_integration/common/synthtrace_es_client_service.ts index 0ff00d415e7ac..b5fe58907cbd8 100644 --- a/x-pack/test/apm_api_integration/common/synthtrace_es_client_service.ts +++ b/x-pack/test/apm_api_integration/common/synthtrace_es_client_service.ts @@ -11,5 +11,6 @@ import { InheritedFtrProviderContext } from './ftr_provider_context'; export async function synthtraceEsClientService(context: InheritedFtrProviderContext) { const es = context.getService('es'); - return new apm.ApmSynthtraceEsClient(es, createLogger(LogLevel.info)); + const forceDataStreams = false; + return new apm.ApmSynthtraceEsClient(es, createLogger(LogLevel.info), forceDataStreams); } diff --git a/x-pack/test/apm_api_integration/tests/anomalies/anomaly_charts.spec.ts b/x-pack/test/apm_api_integration/tests/anomalies/anomaly_charts.spec.ts index 174d3b9fa6dff..00bdc258fb40f 100644 --- a/x-pack/test/apm_api_integration/tests/anomalies/anomaly_charts.spec.ts +++ b/x-pack/test/apm_api_integration/tests/anomalies/anomaly_charts.spec.ts @@ -104,7 +104,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const events = timerange(new Date(start).getTime(), new Date(end).getTime()) .interval('1m') .rate(1) - .flatMap((timestamp) => { + .spans((timestamp) => { const isInSpike = timestamp >= spikeStart && timestamp < spikeEnd; const count = isInSpike ? 4 : NORMAL_RATE; const duration = isInSpike ? 1000 : NORMAL_DURATION; diff --git a/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/generate_data.ts b/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/generate_data.ts index e081c5ea2e168..723083e932117 100644 --- a/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/generate_data.ts @@ -29,36 +29,36 @@ export async function generateData({ const { transactionName, duration, serviceName } = dataConfig; const instance = apm.service(serviceName, 'production', 'go').instance('instance-a'); - const traceEvents = [ - ...timerange(start, end) - .interval('1m') - .rate(coldStartRate) - .flatMap((timestamp) => [ - ...instance - .transaction(transactionName) - .defaults({ - 'faas.coldstart': true, - }) - .timestamp(timestamp) - .duration(duration) - .success() - .serialize(), - ]), - ...timerange(start, end) - .interval('1m') - .rate(warmStartRate) - .flatMap((timestamp) => [ - ...instance - .transaction(transactionName) - .defaults({ - 'faas.coldstart': false, - }) - .timestamp(timestamp) - .duration(duration) - .success() - .serialize(), - ]), - ]; + const traceEvents = timerange(start, end) + .interval('1m') + .rate(coldStartRate) + .spans((timestamp) => + instance + .transaction(transactionName) + .defaults({ + 'faas.coldstart': true, + }) + .timestamp(timestamp) + .duration(duration) + .success() + .serialize() + ) + .concat( + timerange(start, end) + .interval('1m') + .rate(warmStartRate) + .spans((timestamp) => + instance + .transaction(transactionName) + .defaults({ + 'faas.coldstart': false, + }) + .timestamp(timestamp) + .duration(duration) + .success() + .serialize() + ) + ); await synthtraceEsClient.index(traceEvents); } diff --git a/x-pack/test/apm_api_integration/tests/cold_start/generate_data.ts b/x-pack/test/apm_api_integration/tests/cold_start/generate_data.ts index 50861da61a9f7..1270f9bed02e8 100644 --- a/x-pack/test/apm_api_integration/tests/cold_start/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/cold_start/generate_data.ts @@ -36,11 +36,11 @@ export async function generateData({ const instance = apm.service(serviceName, 'production', 'go').instance('instance-a'); const traceEvents = [ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(coldStartRate) - .flatMap((timestamp) => [ - ...instance + .spans((timestamp) => + instance .transaction(coldStartTransaction.name) .defaults({ 'faas.coldstart': true, @@ -48,13 +48,13 @@ export async function generateData({ .timestamp(timestamp) .duration(coldStartTransaction.duration) .success() - .serialize(), - ]), - ...timerange(start, end) + .serialize() + ), + timerange(start, end) .interval('1m') .rate(warmStartRate) - .flatMap((timestamp) => [ - ...instance + .spans((timestamp) => + instance .transaction(warmStartTransaction.name) .defaults({ 'faas.coldstart': false, @@ -62,8 +62,8 @@ export async function generateData({ .timestamp(timestamp) .duration(warmStartTransaction.duration) .success() - .serialize(), - ]), + .serialize() + ), ]; await synthtraceEsClient.index(traceEvents); diff --git a/x-pack/test/apm_api_integration/tests/data_view/static.spec.ts b/x-pack/test/apm_api_integration/tests/data_view/static.spec.ts index ee4bad9c1e79c..64472022c4f99 100644 --- a/x-pack/test/apm_api_integration/tests/data_view/static.spec.ts +++ b/x-pack/test/apm_api_integration/tests/data_view/static.spec.ts @@ -139,11 +139,11 @@ function generateApmData(synthtrace: ApmSynthtraceEsClient) { const instance = apm.service('multiple-env-service', 'production', 'go').instance('my-instance'); return synthtrace.index([ - ...range + range .interval('1s') .rate(1) - .flatMap((timestamp) => [ - ...instance.transaction('GET /api').timestamp(timestamp).duration(30).success().serialize(), - ]), + .spans((timestamp) => + instance.transaction('GET /api').timestamp(timestamp).duration(30).success().serialize() + ), ]); } diff --git a/x-pack/test/apm_api_integration/tests/dependencies/generate_data.ts b/x-pack/test/apm_api_integration/tests/dependencies/generate_data.ts index 21af5d91d14e1..b12dc10f8a874 100644 --- a/x-pack/test/apm_api_integration/tests/dependencies/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/dependencies/generate_data.ts @@ -37,7 +37,7 @@ export async function generateData({ timerange(start, end) .interval('1m') .rate(rate) - .flatMap((timestamp) => + .spans((timestamp) => instance .transaction(transaction.name) .timestamp(timestamp) diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts index 9e649d37d6a9d..f8202a4c51699 100644 --- a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts @@ -128,10 +128,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { const transactionNameProductId = 'GET /api/product/:id'; await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_LIST_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductList) .timestamp(timestamp) @@ -139,10 +139,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_LIST_ERROR_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductList) .duration(1000) @@ -150,10 +150,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .failure() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_ID_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductId) .timestamp(timestamp) @@ -161,10 +161,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_ID_ERROR_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductId) .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts b/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts index 4dddff70958ba..b943952a1b68e 100644 --- a/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts +++ b/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts @@ -77,10 +77,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { const transactionNameProductId = 'GET /api/product/:id'; await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_LIST_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductList, 'Worker') .timestamp(timestamp) @@ -88,10 +88,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_LIST_ERROR_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductList, 'Worker') .duration(1000) @@ -99,10 +99,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .failure() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_ID_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductId) .timestamp(timestamp) @@ -110,10 +110,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_ID_ERROR_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductId) .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/errors/error_group_list.spec.ts b/x-pack/test/apm_api_integration/tests/errors/error_group_list.spec.ts index 7ec0380429cb9..7ce10da82c1d5 100644 --- a/x-pack/test/apm_api_integration/tests/errors/error_group_list.spec.ts +++ b/x-pack/test/apm_api_integration/tests/errors/error_group_list.spec.ts @@ -76,10 +76,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .instance('instance-a'); await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(appleTransaction.successRate) - .flatMap((timestamp) => + .spans((timestamp) => serviceInstance .transaction(appleTransaction.name) .timestamp(timestamp) @@ -87,10 +87,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(appleTransaction.failureRate) - .flatMap((timestamp) => + .spans((timestamp) => serviceInstance .transaction(appleTransaction.name) .errors(serviceInstance.error('error 1', 'foo').timestamp(timestamp)) @@ -99,10 +99,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .failure() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(bananaTransaction.successRate) - .flatMap((timestamp) => + .spans((timestamp) => serviceInstance .transaction(bananaTransaction.name) .timestamp(timestamp) @@ -110,10 +110,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(bananaTransaction.failureRate) - .flatMap((timestamp) => + .spans((timestamp) => serviceInstance .transaction(bananaTransaction.name) .errors(serviceInstance.error('error 2', 'bar').timestamp(timestamp)) diff --git a/x-pack/test/apm_api_integration/tests/errors/generate_data.ts b/x-pack/test/apm_api_integration/tests/errors/generate_data.ts index b9ac77ca34425..2daef094691f5 100644 --- a/x-pack/test/apm_api_integration/tests/errors/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/errors/generate_data.ts @@ -37,24 +37,23 @@ export async function generateData({ const { bananaTransaction, appleTransaction } = config; - const documents = [appleTransaction, bananaTransaction] - .map((transaction, index) => { - return [ - ...timerange(start, end) - .interval(interval) - .rate(transaction.successRate) - .flatMap((timestamp) => - serviceGoProdInstance - .transaction(transaction.name) - .timestamp(timestamp) - .duration(1000) - .success() - .serialize() - ), - ...timerange(start, end) + const documents = [appleTransaction, bananaTransaction].map((transaction, index) => { + return timerange(start, end) + .interval(interval) + .rate(transaction.successRate) + .spans((timestamp) => + serviceGoProdInstance + .transaction(transaction.name) + .timestamp(timestamp) + .duration(1000) + .success() + .serialize() + ) + .concat( + timerange(start, end) .interval(interval) .rate(transaction.failureRate) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transaction.name) .errors( @@ -64,10 +63,9 @@ export async function generateData({ .timestamp(timestamp) .failure() .serialize() - ), - ]; - }) - .flatMap((_) => _); + ) + ); + }); await synthtraceEsClient.index(documents); } diff --git a/x-pack/test/apm_api_integration/tests/latency/service_apis.spec.ts b/x-pack/test/apm_api_integration/tests/latency/service_apis.spec.ts index bf2127e544107..bd47450d26ccf 100644 --- a/x-pack/test/apm_api_integration/tests/latency/service_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/latency/service_apis.spec.ts @@ -130,20 +130,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { .instance('instance-b'); await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction('GET /api/product/list') .duration(GO_PROD_DURATION) .timestamp(timestamp) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_DEV_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoDevInstance .transaction('GET /api/product/:id') .duration(GO_DEV_DURATION) diff --git a/x-pack/test/apm_api_integration/tests/latency/service_maps.spec.ts b/x-pack/test/apm_api_integration/tests/latency/service_maps.spec.ts index f977c35bfa54f..23978d2682fc4 100644 --- a/x-pack/test/apm_api_integration/tests/latency/service_maps.spec.ts +++ b/x-pack/test/apm_api_integration/tests/latency/service_maps.spec.ts @@ -76,20 +76,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { .instance('instance-b'); await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction('GET /api/product/list', 'Worker') .duration(GO_PROD_DURATION) .timestamp(timestamp) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_DEV_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoDevInstance .transaction('GET /api/product/:id') .duration(GO_DEV_DURATION) diff --git a/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.spec.ts b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.spec.ts index 8956438855e1a..ec31e520970ae 100644 --- a/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.spec.ts +++ b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.spec.ts @@ -103,30 +103,30 @@ export default function ApiTest({ getService }: FtrProviderContext) { .instance('instance-c'); await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction('GET /api/product/list') .duration(1000) .timestamp(timestamp) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_DEV_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoDevInstance .transaction('GET /api/product/:id') .duration(1000) .timestamp(timestamp) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(JAVA_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceJavaInstance .transaction('POST /api/product/buy') .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/service_nodes/get_service_nodes.spec.ts b/x-pack/test/apm_api_integration/tests/service_nodes/get_service_nodes.spec.ts index 8dac8f7dea4f9..87207211c6ca8 100644 --- a/x-pack/test/apm_api_integration/tests/service_nodes/get_service_nodes.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_nodes/get_service_nodes.spec.ts @@ -58,7 +58,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { timerange(start, end) .interval('1m') .rate(1) - .flatMap((timestamp) => + .spans((timestamp) => instance .appMetrics({ 'system.process.cpu.total.norm.pct': 1, diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.spec.ts b/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.spec.ts index ec8534de9f913..4f847011ad56e 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.spec.ts @@ -321,7 +321,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { } return synthtraceEsClient.index([ - ...interval.rate(GO_A_INSTANCE_RATE_SUCCESS).flatMap((timestamp) => + interval.rate(GO_A_INSTANCE_RATE_SUCCESS).spans((timestamp) => goInstanceA .transaction('GET /api/product/list') .success() @@ -330,7 +330,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .children(...withSpans(timestamp)) .serialize() ), - ...interval.rate(GO_A_INSTANCE_RATE_FAILURE).flatMap((timestamp) => + interval.rate(GO_A_INSTANCE_RATE_FAILURE).spans((timestamp) => goInstanceA .transaction('GET /api/product/list') .failure() @@ -339,7 +339,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .children(...withSpans(timestamp)) .serialize() ), - ...interval.rate(GO_B_INSTANCE_RATE_SUCCESS).flatMap((timestamp) => + interval.rate(GO_B_INSTANCE_RATE_SUCCESS).spans((timestamp) => goInstanceB .transaction('GET /api/product/list') .success() @@ -348,7 +348,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .children(...withSpans(timestamp)) .serialize() ), - ...interval.rate(JAVA_INSTANCE_RATE).flatMap((timestamp) => + interval.rate(JAVA_INSTANCE_RATE).spans((timestamp) => javaInstance .transaction('GET /api/product/list') .success() diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups/generate_data.ts b/x-pack/test/apm_api_integration/tests/services/error_groups/generate_data.ts index ef77cd4003a16..658c8d89e3385 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups/generate_data.ts @@ -42,10 +42,10 @@ export async function generateData({ } = config; await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(PROD_LIST_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductList) .timestamp(timestamp) @@ -53,10 +53,10 @@ export async function generateData({ .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(PROD_LIST_ERROR_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductList) .errors(serviceGoProdInstance.error(ERROR_NAME_1, 'foo').timestamp(timestamp)) @@ -65,10 +65,10 @@ export async function generateData({ .failure() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(PROD_ID_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductId) .timestamp(timestamp) @@ -76,10 +76,10 @@ export async function generateData({ .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(PROD_ID_ERROR_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionNameProductId) .errors(serviceGoProdInstance.error(ERROR_NAME_2, 'bar').timestamp(timestamp)) diff --git a/x-pack/test/apm_api_integration/tests/services/get_service_node_metadata.spec.ts b/x-pack/test/apm_api_integration/tests/services/get_service_node_metadata.spec.ts index 1317ce7e9a1ee..b5bbd3a565a84 100644 --- a/x-pack/test/apm_api_integration/tests/services/get_service_node_metadata.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/get_service_node_metadata.spec.ts @@ -62,7 +62,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { timerange(start, end) .interval('1m') .rate(1) - .flatMap((timestamp) => + .spans((timestamp) => instance .transaction('GET /api/product/list') .timestamp(timestamp) diff --git a/x-pack/test/apm_api_integration/tests/services/service_details/generate_data.ts b/x-pack/test/apm_api_integration/tests/services/service_details/generate_data.ts index f6035cf784dbd..d0c29823e4f26 100644 --- a/x-pack/test/apm_api_integration/tests/services/service_details/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_details/generate_data.ts @@ -68,10 +68,10 @@ export async function generateData({ const instance = apm.service(serviceName, 'production', agentName).instance('instance-a'); const traceEvents = [ - ...timerange(start, end) + timerange(start, end) .interval('30s') .rate(rate) - .flatMap((timestamp) => + .spans((timestamp) => instance .transaction(transaction.name) .timestamp(timestamp) @@ -96,10 +96,10 @@ export async function generateData({ .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('30s') .rate(rate) - .flatMap((timestamp) => + .spans((timestamp) => instance .transaction(transaction.name) .timestamp(timestamp) diff --git a/x-pack/test/apm_api_integration/tests/services/service_icons/generate_data.ts b/x-pack/test/apm_api_integration/tests/services/service_icons/generate_data.ts index 93f68242caa5e..fc94368acc8e7 100644 --- a/x-pack/test/apm_api_integration/tests/services/service_icons/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_icons/generate_data.ts @@ -38,7 +38,7 @@ export async function generateData({ const traceEvents = timerange(start, end) .interval('30s') .rate(rate) - .flatMap((timestamp) => + .spans((timestamp) => instance .transaction(transaction.name) .defaults({ diff --git a/x-pack/test/apm_api_integration/tests/services/throughput.spec.ts b/x-pack/test/apm_api_integration/tests/services/throughput.spec.ts index 48887b751aa02..a2428d13ae253 100644 --- a/x-pack/test/apm_api_integration/tests/services/throughput.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/throughput.spec.ts @@ -85,30 +85,30 @@ export default function ApiTest({ getService }: FtrProviderContext) { .instance('instance-c'); await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction('GET /api/product/list') .duration(1000) .timestamp(timestamp) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_DEV_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoDevInstance .transaction('GET /api/product/:id') .duration(1000) .timestamp(timestamp) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(JAVA_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceJavaInstance .transaction('POST /api/product/buy') .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/services/top_services.spec.ts b/x-pack/test/apm_api_integration/tests/services/top_services.spec.ts index 801faf5331682..3d46a022ff8ed 100644 --- a/x-pack/test/apm_api_integration/tests/services/top_services.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/top_services.spec.ts @@ -96,9 +96,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { return synthtrace.index([ - ...transactionInterval + transactionInterval .rate(config.multiple.prod.rps) - .flatMap((timestamp) => [ + .spans((timestamp) => [ ...multipleEnvServiceProdInstance .transaction('GET /api') .timestamp(timestamp) @@ -106,9 +106,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize(), ]), - ...transactionInterval + transactionInterval .rate(config.multiple.dev.rps) - .flatMap((timestamp) => [ + .spans((timestamp) => [ ...multipleEnvServiceDevInstance .transaction('GET /api') .timestamp(timestamp) @@ -116,9 +116,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { .failure() .serialize(), ]), - ...transactionInterval + transactionInterval .rate(config.multiple.prod.rps) - .flatMap((timestamp) => [ + .spans((timestamp) => [ ...multipleEnvServiceDevInstance .transaction('non-request', 'rpc') .timestamp(timestamp) @@ -126,7 +126,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize(), ]), - ...metricInterval.rate(1).flatMap((timestamp) => [ + metricInterval.rate(1).spans((timestamp) => [ ...metricOnlyInstance .appMetrics({ 'system.memory.actual.free': 1, @@ -137,9 +137,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { .timestamp(timestamp) .serialize(), ]), - ...errorInterval + errorInterval .rate(1) - .flatMap((timestamp) => [ + .spans((timestamp) => [ ...errorOnlyInstance.error('Foo').timestamp(timestamp).serialize(), ]), ]); diff --git a/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.spec.ts b/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.spec.ts index 867263257f30f..9b24a73a50ee2 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.spec.ts @@ -101,10 +101,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .instance('instance-c'); await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction('GET /api/product/list') .duration(1000) @@ -132,10 +132,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { ) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(JAVA_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceJavaInstance .transaction('POST /api/product/buy') .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts b/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts index 2b3ba22d28d69..f460bb700caff 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts @@ -116,20 +116,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { .instance('instance-b'); await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction('GET /api/product/list') .duration(1000) .timestamp(timestamp) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_DEV_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoDevInstance .transaction('GET /api/product/:id') .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts b/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts index adbae6dff2096..147371be3d64e 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts +++ b/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts @@ -76,20 +76,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { .instance('instance-b'); await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction('GET /api/product/list', 'Worker') .duration(1000) .timestamp(timestamp) .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_DEV_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoDevInstance .transaction('GET /api/product/:id') .duration(1000) diff --git a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.spec.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.spec.ts index 83f4bc6ea4afb..02103b456c826 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.spec.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.spec.ts @@ -91,10 +91,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { const transactionName = 'GET /api/product/list'; await synthtraceEsClient.index([ - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionName) .timestamp(timestamp) @@ -102,10 +102,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success() .serialize() ), - ...timerange(start, end) + timerange(start, end) .interval('1m') .rate(GO_PROD_ERROR_RATE) - .flatMap((timestamp) => + .spans((timestamp) => serviceGoProdInstance .transaction(transactionName) .duration(1000) From bf12417632b06b49d22bea8681e5442c7036c48b Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Tue, 8 Mar 2022 13:01:42 +0100 Subject: [PATCH 053/140] [Security Solution] Add User Risk tab to Users page (#126434) * Add User Risk tab to Users Page * Refactor riskyHosts endpoint to support user risk score * Reorganize risk score folder sctructure Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../security_solution/common/constants.ts | 2 + .../security_solution/hosts/all/index.ts | 1 - .../security_solution/hosts/common/index.ts | 19 -- .../security_solution/hosts/index.ts | 2 - .../security_solution/hosts/kpi/index.ts | 2 - .../hosts/kpi/risky_hosts/index.ts | 27 --- .../hosts/risk_score/index.ts | 58 ----- .../security_solution/index.ts | 29 ++- .../security_solution/risk_score/all/index.ts | 86 +++++++ .../common}/index.test.ts | 8 +- .../risk_score/common/index.ts | 27 +++ .../security_solution/risk_score/index.ts | 10 + .../security_solution/risk_score/kpi/index.ts | 29 +++ .../security_solution/users/common/index.ts | 22 ++ .../integration/users/all_users_tab.spec.ts | 29 +++ .../cypress/integration/users/inspect.spec.ts | 16 +- .../users/users_anomalies_tab.spec.ts | 26 ++ .../users/users_risk_score_tab.spec.ts | 26 ++ .../cypress/screens/hosts/host_risk.ts | 5 +- .../cypress/screens/users/all_users.ts | 4 + .../cypress/screens/users/user_anomalies.ts | 9 + .../cypress/screens/users/user_risk_score.ts | 9 + .../cti_details/host_risk_summary.tsx | 11 +- .../cti_details/threat_summary_view.tsx | 2 +- .../event_details/event_details.tsx | 2 +- .../public/common/components/links/index.tsx | 12 +- .../ml/tables/anomalies_user_table.tsx | 2 +- .../severity/common/index.test.tsx} | 33 +-- .../components/severity/common/index.tsx} | 31 +-- .../components/severity}/severity_badges.tsx | 12 +- .../components/severity}/severity_bar.tsx | 12 +- .../severity}/severity_filter_group.tsx | 63 ++--- .../common/components/severity/types.ts | 12 + .../anomalies_query_tab_body/index.tsx | 2 + .../anomalies_query_tab_body/types.ts | 1 + .../public/common/mock/global_state.ts | 20 +- .../host_risk_information/index.tsx | 22 +- .../host_risk_score_table/columns.tsx | 8 +- .../host_risk_score_table/index.tsx | 57 ++++- .../host_risk_score_table/translations.ts | 1 + .../host_score_over_time/index.test.tsx | 4 +- .../components/host_score_over_time/index.tsx | 6 +- .../hosts/components/hosts_table/columns.tsx | 10 +- .../hosts/components/hosts_table/index.tsx | 7 +- .../hosts/components/kpi_hosts/index.tsx | 2 +- .../kpi_hosts/risky_hosts/index.test.tsx | 10 +- .../kpi_hosts/risky_hosts/index.tsx | 19 +- .../index.test.tsx | 4 +- .../top_host_score_contributors/index.tsx | 16 +- .../kpi_hosts/risky_hosts/index.tsx | 131 ---------- .../navigation/host_risk_score_tab_body.tsx | 12 +- .../public/hosts/store/actions.ts | 11 +- .../public/hosts/store/helpers.test.ts | 6 +- .../public/hosts/store/helpers.ts | 4 +- .../public/hosts/store/model.ts | 14 +- .../public/hosts/store/reducer.ts | 6 +- .../components/host_overview/index.test.tsx | 4 +- .../components/host_overview/index.tsx | 18 +- .../overview_risky_host_links/index.test.tsx | 4 +- .../overview_risky_host_links/index.tsx | 3 +- .../risky_hosts_panel_view.tsx | 2 +- .../public/overview/pages/overview.test.tsx | 4 +- .../containers/all}/index.tsx | 137 +++++++---- .../containers/all}/translations.ts | 0 .../risk_score/containers/common/index.ts | 22 ++ .../containers/index.ts} | 9 +- .../risk_score/containers/kpi/index.tsx | 165 +++++++++++++ .../event_details/expandable_event.tsx | 3 +- .../side_panel/event_details/index.tsx | 6 +- .../user_risk_score_table/columns.test.tsx | 54 +++++ .../user_risk_score_table/columns.tsx | 112 +++++++++ .../user_risk_score_table/index.test.tsx | 55 +++++ .../user_risk_score_table/index.tsx | 228 ++++++++++++++++++ .../user_risk_score_table/translations.ts | 60 +++++ .../public/users/pages/constants.ts | 4 +- .../users/pages/details/details_tabs.tsx | 81 +++++-- .../public/users/pages/details/nav_tabs.tsx | 14 +- .../public/users/pages/details/types.ts | 2 +- .../public/users/pages/details/utils.ts | 1 + .../public/users/pages/index.tsx | 2 +- .../public/users/pages/nav_tabs.tsx | 6 + .../navigation/all_users_query_tab_body.tsx | 4 +- .../public/users/pages/navigation/types.ts | 4 +- .../navigation/user_risk_score_tab_body.tsx | 75 ++++++ .../public/users/pages/translations.ts | 7 + .../public/users/pages/users.tsx | 27 ++- .../public/users/pages/users_tabs.tsx | 5 + .../public/users/store/actions.ts | 15 +- .../public/users/store/model.ts | 22 +- .../public/users/store/reducer.ts | 82 ++++--- .../public/users/store/selectors.ts | 6 + .../factory/hosts/all/index.test.ts | 6 +- .../factory/hosts/all/index.ts | 14 +- .../factory/hosts/index.test.ts | 5 - .../security_solution/factory/hosts/index.ts | 4 - .../factory/hosts/kpi/risky_hosts/index.ts | 53 ---- .../security_solution/factory/index.ts | 2 + .../risk_score => risk_score/all}/index.ts | 20 +- .../all/query.risk_score.dsl.ts} | 22 +- .../factory/risk_score/index.ts | 16 ++ .../kpi}/__mocks__/index.ts | 12 +- .../kpi}/index.test.ts | 13 +- .../factory/risk_score/kpi/index.ts | 53 ++++ .../kpi/query.kpi_risk_score.dsl.ts} | 13 +- 104 files changed, 1748 insertions(+), 739 deletions(-) delete mode 100644 x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/risky_hosts/index.ts delete mode 100644 x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts create mode 100644 x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts rename x-pack/plugins/security_solution/common/search_strategy/security_solution/{hosts/risk_score => risk_score/common}/index.test.ts (58%) create mode 100644 x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts create mode 100644 x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/index.ts create mode 100644 x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/kpi/index.ts create mode 100644 x-pack/plugins/security_solution/common/search_strategy/security_solution/users/common/index.ts create mode 100644 x-pack/plugins/security_solution/cypress/integration/users/all_users_tab.spec.ts create mode 100644 x-pack/plugins/security_solution/cypress/integration/users/users_anomalies_tab.spec.ts create mode 100644 x-pack/plugins/security_solution/cypress/integration/users/users_risk_score_tab.spec.ts create mode 100644 x-pack/plugins/security_solution/cypress/screens/users/user_anomalies.ts create mode 100644 x-pack/plugins/security_solution/cypress/screens/users/user_risk_score.ts rename x-pack/plugins/security_solution/public/{hosts/components/common/host_risk_score.test.tsx => common/components/severity/common/index.test.tsx} (70%) rename x-pack/plugins/security_solution/public/{hosts/components/common/host_risk_score.tsx => common/components/severity/common/index.tsx} (62%) rename x-pack/plugins/security_solution/public/{hosts/components/host_risk_score_table => common/components/severity}/severity_badges.tsx (74%) rename x-pack/plugins/security_solution/public/{hosts/components/host_risk_score_table => common/components/severity}/severity_bar.tsx (76%) rename x-pack/plugins/security_solution/public/{hosts/components/host_risk_score_table => common/components/severity}/severity_filter_group.tsx (57%) create mode 100644 x-pack/plugins/security_solution/public/common/components/severity/types.ts delete mode 100644 x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/risky_hosts/index.tsx rename x-pack/plugins/security_solution/public/{hosts/containers/host_risk_score => risk_score/containers/all}/index.tsx (64%) rename x-pack/plugins/security_solution/public/{hosts/containers/host_risk_score => risk_score/containers/all}/translations.ts (100%) create mode 100644 x-pack/plugins/security_solution/public/risk_score/containers/common/index.ts rename x-pack/plugins/security_solution/public/{common/containers/hosts_risk/types.ts => risk_score/containers/index.ts} (74%) create mode 100644 x-pack/plugins/security_solution/public/risk_score/containers/kpi/index.tsx create mode 100644 x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.test.tsx create mode 100644 x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.tsx create mode 100644 x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx create mode 100644 x-pack/plugins/security_solution/public/users/components/user_risk_score_table/translations.ts create mode 100644 x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_score_tab_body.tsx delete mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/index.ts rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/{hosts/risk_score => risk_score/all}/index.ts (69%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/{hosts/risk_score/query.hosts_risk.dsl.ts => risk_score/all/query.risk_score.dsl.ts} (75%) create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/index.ts rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/{hosts/kpi/risky_hosts => risk_score/kpi}/__mocks__/index.ts (66%) rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/{hosts/kpi/risky_hosts => risk_score/kpi}/index.test.ts (50%) create mode 100644 x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/index.ts rename x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/{hosts/kpi/risky_hosts/query.hosts_kpi_risky_hosts.dsl.ts => risk_score/kpi/query.kpi_risk_score.dsl.ts} (68%) diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index cad2985109538..bc44b34021325 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -364,6 +364,8 @@ export const ELASTIC_NAME = 'estc' as const; export const RISKY_HOSTS_INDEX_PREFIX = 'ml_host_risk_score_' as const; +export const RISKY_USERS_INDEX_PREFIX = 'ml_user_risk_score_' as const; + export const TRANSFORM_STATES = { ABORTING: 'aborting', FAILED: 'failed', diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/all/index.ts index feb2f6e19e491..2a1261cebad2c 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/all/index.ts @@ -29,6 +29,5 @@ export interface HostsRequestOptions extends RequestOptionsPaginated; - -export const enum HostRiskScoreFields { - timestamp = '@timestamp', - hostName = 'host.name', - riskScore = 'risk_stats.risk_score', - risk = 'risk', - // TODO: Steph/Host Risk - // ruleRisks = 'rule_risks', -} - -export interface HostRiskScoreItem { - _id?: Maybe; - [HostRiskScoreFields.hostName]: Maybe; - [HostRiskScoreFields.risk]: Maybe; - [HostRiskScoreFields.riskScore]: Maybe; - // TODO: Steph/Host Risk - // [HostRiskScoreFields.ruleRisks]: Maybe; -} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts index 7495e2dd2b865..bae99649c2e01 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts @@ -11,7 +11,6 @@ export * from './common'; export * from './details'; export * from './first_last_seen'; export * from './kpi'; -export * from './risk_score'; export * from './overview'; export * from './uncommon_processes'; @@ -23,6 +22,5 @@ export enum HostsQueries { hosts = 'hosts', hostsEntities = 'hostsEntities', overview = 'overviewHost', - hostsRiskScore = 'hostsRiskScore', uncommonProcesses = 'uncommonProcesses', } diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts index 2acbce2c88653..d48172bebee4c 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts @@ -9,7 +9,6 @@ export * from './authentications'; export * from './common'; export * from './hosts'; export * from './unique_ips'; -export * from './risky_hosts'; import { HostsKpiAuthenticationsStrategyResponse } from './authentications'; import { HostsKpiHostsStrategyResponse } from './hosts'; @@ -21,7 +20,6 @@ export enum HostsKpiQueries { kpiHosts = 'hostsKpiHosts', kpiHostsEntities = 'hostsKpiHostsEntities', kpiUniqueIps = 'hostsKpiUniqueIps', - kpiRiskyHosts = 'hostsKpiRiskyHosts', kpiUniqueIpsEntities = 'hostsKpiUniqueIpsEntities', } diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/risky_hosts/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/risky_hosts/index.ts deleted file mode 100644 index 610077bd6bc65..0000000000000 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/risky_hosts/index.ts +++ /dev/null @@ -1,27 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; -import type { Inspect, Maybe } from '../../../../common'; -import type { RequestBasicOptions } from '../../..'; - -export type HostsKpiRiskyHostsRequestOptions = RequestBasicOptions; - -export interface HostsKpiRiskyHostsStrategyResponse extends IEsSearchResponse { - inspect?: Maybe; - riskyHosts: { - [key in HostRiskSeverity]: number; - }; -} - -export const enum HostRiskSeverity { - unknown = 'Unknown', - low = 'Low', - moderate = 'Moderate', - high = 'High', - critical = 'Critical', -} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts deleted file mode 100644 index b36fbd5ce57a3..0000000000000 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts +++ /dev/null @@ -1,58 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FactoryQueryTypes, HostRiskScoreFields } from '../..'; -import type { - IEsSearchRequest, - IEsSearchResponse, -} from '../../../../../../../../src/plugins/data/common'; -import { RISKY_HOSTS_INDEX_PREFIX } from '../../../../constants'; -import { ESQuery } from '../../../../typed_json'; -import { Inspect, Maybe, SortField, TimerangeInput } from '../../../common'; - -export interface HostsRiskScoreRequestOptions extends IEsSearchRequest { - defaultIndex: string[]; - factoryQueryType?: FactoryQueryTypes; - hostNames?: string[]; - timerange?: TimerangeInput; - onlyLatest?: boolean; - pagination?: { - cursorStart: number; - querySize: number; - }; - sort?: HostRiskScoreSortField; - filterQuery?: ESQuery | string | undefined; -} - -export interface HostsRiskScoreStrategyResponse extends IEsSearchResponse { - inspect?: Maybe; - totalCount: number; -} - -export interface HostsRiskScore { - '@timestamp': string; - host: { - name: string; - }; - risk: string; - risk_stats: { - rule_risks: RuleRisk[]; - risk_score: number; - }; -} - -export interface RuleRisk { - rule_name: string; - rule_risk: number; - rule_id?: string; // TODO Remove the '?' when the new transform is delivered -} - -export const getHostRiskIndex = (spaceId: string, onlyLatest: boolean = true): string => { - return `${RISKY_HOSTS_INDEX_PREFIX}${onlyLatest ? 'latest_' : ''}${spaceId}`; -}; - -export type HostRiskScoreSortField = SortField; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts index eee621d02838f..e57e0a95d3ec6 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts @@ -28,8 +28,6 @@ import { HostsKpiUniqueIpsStrategyResponse, HostsKpiUniqueIpsRequestOptions, HostFirstLastSeenRequestOptions, - HostsRiskScoreStrategyResponse, - HostsRiskScoreRequestOptions, } from './hosts'; import { NetworkQueries, @@ -77,12 +75,16 @@ import { } from './cti'; import { - HostsKpiRiskyHostsRequestOptions, - HostsKpiRiskyHostsStrategyResponse, -} from './hosts/kpi/risky_hosts'; + RiskScoreStrategyResponse, + RiskQueries, + RiskScoreRequestOptions, + KpiRiskScoreStrategyResponse, + KpiRiskScoreRequestOptions, +} from './risk_score'; export * from './cti'; export * from './hosts'; +export * from './risk_score'; export * from './matrix_histogram'; export * from './network'; @@ -91,6 +93,7 @@ export type FactoryQueryTypes = | HostsKpiQueries | NetworkQueries | NetworkKpiQueries + | RiskQueries | CtiQueries | typeof MatrixHistogramQuery | typeof MatrixHistogramQueryEntities; @@ -119,8 +122,6 @@ export type StrategyResponseType = T extends HostsQ ? HostsStrategyResponse : T extends HostsQueries.details ? HostDetailsStrategyResponse - : T extends HostsQueries.hostsRiskScore - ? HostsRiskScoreStrategyResponse : T extends HostsQueries.overview ? HostsOverviewStrategyResponse : T extends HostsQueries.authentications @@ -133,8 +134,6 @@ export type StrategyResponseType = T extends HostsQ ? HostsKpiAuthenticationsStrategyResponse : T extends HostsKpiQueries.kpiHosts ? HostsKpiHostsStrategyResponse - : T extends HostsKpiQueries.kpiRiskyHosts - ? HostsKpiRiskyHostsStrategyResponse : T extends HostsKpiQueries.kpiUniqueIps ? HostsKpiUniqueIpsStrategyResponse : T extends NetworkQueries.details @@ -169,12 +168,14 @@ export type StrategyResponseType = T extends HostsQ ? CtiEventEnrichmentStrategyResponse : T extends CtiQueries.dataSource ? CtiDataSourceStrategyResponse + : T extends RiskQueries.riskScore + ? RiskScoreStrategyResponse + : T extends RiskQueries.kpiRiskScore + ? KpiRiskScoreStrategyResponse : never; export type StrategyRequestType = T extends HostsQueries.hosts ? HostsRequestOptions - : T extends HostsQueries.hostsRiskScore - ? HostsRiskScoreRequestOptions : T extends HostsQueries.details ? HostDetailsRequestOptions : T extends HostsQueries.overview @@ -191,8 +192,6 @@ export type StrategyRequestType = T extends HostsQu ? HostsKpiHostsRequestOptions : T extends HostsKpiQueries.kpiUniqueIps ? HostsKpiUniqueIpsRequestOptions - : T extends HostsKpiQueries.kpiRiskyHosts - ? HostsKpiRiskyHostsRequestOptions : T extends NetworkQueries.details ? NetworkDetailsRequestOptions : T extends NetworkQueries.dns @@ -225,6 +224,10 @@ export type StrategyRequestType = T extends HostsQu ? CtiEventEnrichmentRequestOptions : T extends CtiQueries.dataSource ? CtiDataSourceRequestOptions + : T extends RiskQueries.riskScore + ? RiskScoreRequestOptions + : T extends RiskQueries.kpiRiskScore + ? KpiRiskScoreRequestOptions : never; export interface DocValueFieldsInput { diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts new file mode 100644 index 0000000000000..4c65f9db1b14a --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FactoryQueryTypes } from '../..'; +import type { + IEsSearchRequest, + IEsSearchResponse, +} from '../../../../../../../../src/plugins/data/common'; + +import { ESQuery } from '../../../../typed_json'; +import { Inspect, Maybe, SortField, TimerangeInput } from '../../../common'; + +export interface RiskScoreRequestOptions extends IEsSearchRequest { + defaultIndex: string[]; + factoryQueryType?: FactoryQueryTypes; + timerange?: TimerangeInput; + onlyLatest?: boolean; + pagination?: { + cursorStart: number; + querySize: number; + }; + sort?: RiskScoreSortField; + filterQuery?: ESQuery | string | undefined; +} + +export interface RiskScoreStrategyResponse extends IEsSearchResponse { + inspect?: Maybe; + totalCount: number; +} + +export interface RiskScore { + '@timestamp': string; + risk: string; + risk_stats: { + rule_risks: RuleRisk[]; + risk_score: number; + }; +} + +export interface HostsRiskScore extends RiskScore { + host: { + name: string; + }; +} + +export interface UsersRiskScore extends RiskScore { + user: { + name: string; + }; +} + +export interface RuleRisk { + rule_name: string; + rule_risk: number; + rule_id: string; +} + +export type RiskScoreSortField = SortField; + +export const enum RiskScoreFields { + timestamp = '@timestamp', + hostName = 'host.name', + userName = 'user.name', + riskScore = 'risk_stats.risk_score', + risk = 'risk', +} + +export interface RiskScoreItem { + _id?: Maybe; + [RiskScoreFields.hostName]: Maybe; + [RiskScoreFields.userName]: Maybe; + [RiskScoreFields.risk]: Maybe; + [RiskScoreFields.riskScore]: Maybe; +} + +export const enum RiskSeverity { + unknown = 'Unknown', + low = 'Low', + moderate = 'Moderate', + high = 'High', + critical = 'Critical', +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.test.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.test.ts similarity index 58% rename from x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.test.ts rename to x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.test.ts index 8c58ccaabe8df..c1943059e1536 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.test.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.test.ts @@ -5,10 +5,14 @@ * 2.0. */ -import { getHostRiskIndex } from '.'; +import { getHostRiskIndex, getUserRiskIndex } from './'; describe('hosts risk search_strategy getHostRiskIndex', () => { - it('should properly return index if space is specified', () => { + it('should properly return host index if space is specified', () => { expect(getHostRiskIndex('testName')).toEqual('ml_host_risk_score_latest_testName'); }); + + it('should properly return user index if space is specified', () => { + expect(getUserRiskIndex('testName')).toEqual('ml_user_risk_score_latest_testName'); + }); }); diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts new file mode 100644 index 0000000000000..e3cfdbb0f96b1 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RISKY_HOSTS_INDEX_PREFIX, RISKY_USERS_INDEX_PREFIX } from '../../../../constants'; + +export const getHostRiskIndex = (spaceId: string, onlyLatest: boolean = true): string => { + return `${RISKY_HOSTS_INDEX_PREFIX}${onlyLatest ? 'latest_' : ''}${spaceId}`; +}; + +export const getUserRiskIndex = (spaceId: string, onlyLatest: boolean = true): string => { + return `${RISKY_USERS_INDEX_PREFIX}${onlyLatest ? 'latest_' : ''}${spaceId}`; +}; + +export const buildHostNamesFilter = (hostNames: string[]) => { + return { terms: { 'host.name': hostNames } }; +}; + +export enum RiskQueries { + riskScore = 'riskScore', + kpiRiskScore = 'kpiRiskScore', +} + +export type RiskScoreAggByFields = 'host.name' | 'user.name'; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/index.ts new file mode 100644 index 0000000000000..fd0e3e7af9f01 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './all'; +export * from './common'; +export * from './kpi'; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/kpi/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/kpi/index.ts new file mode 100644 index 0000000000000..895a3d15476f1 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/kpi/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FactoryQueryTypes, RiskScoreAggByFields, RiskSeverity } from '../..'; +import type { + IEsSearchRequest, + IEsSearchResponse, +} from '../../../../../../../../src/plugins/data/common'; +import { ESQuery } from '../../../../typed_json'; + +import { Inspect, Maybe } from '../../../common'; + +export interface KpiRiskScoreRequestOptions extends IEsSearchRequest { + defaultIndex: string[]; + factoryQueryType?: FactoryQueryTypes; + filterQuery?: ESQuery | string | undefined; + aggBy: RiskScoreAggByFields; +} + +export interface KpiRiskScoreStrategyResponse extends IEsSearchResponse { + inspect?: Maybe; + kpiRiskScore: { + [key in RiskSeverity]: number; + }; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/common/index.ts new file mode 100644 index 0000000000000..12f1cddddbd5c --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/common/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Maybe, RiskSeverity } from '../../..'; + +export const enum UserRiskScoreFields { + timestamp = '@timestamp', + userName = 'user.name', + riskScore = 'risk_stats.risk_score', + risk = 'risk', +} + +export interface UserRiskScoreItem { + _id?: Maybe; + [UserRiskScoreFields.userName]: Maybe; + [UserRiskScoreFields.risk]: Maybe; + [UserRiskScoreFields.riskScore]: Maybe; +} diff --git a/x-pack/plugins/security_solution/cypress/integration/users/all_users_tab.spec.ts b/x-pack/plugins/security_solution/cypress/integration/users/all_users_tab.spec.ts new file mode 100644 index 0000000000000..070afc12cf53e --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/users/all_users_tab.spec.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HEADER_SUBTITLE, USER_NAME_CELL } from '../../screens/users/all_users'; +import { cleanKibana } from '../../tasks/common'; + +import { loginAndWaitForPage } from '../../tasks/login'; + +import { USERS_URL } from '../../urls/navigation'; + +describe('Users stats and tables', () => { + before(() => { + cleanKibana(); + + loginAndWaitForPage(USERS_URL); + }); + + it(`renders all users`, () => { + const totalUsers = 35; + const usersPerPage = 10; + + cy.get(HEADER_SUBTITLE).should('have.text', `Showing: ${totalUsers} users`); + cy.get(USER_NAME_CELL).should('have.length', usersPerPage); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/users/inspect.spec.ts b/x-pack/plugins/security_solution/cypress/integration/users/inspect.spec.ts index fc7a68f9730c4..f092949ec56aa 100644 --- a/x-pack/plugins/security_solution/cypress/integration/users/inspect.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/users/inspect.spec.ts @@ -10,7 +10,7 @@ import { ALL_USERS_TABLE } from '../../screens/users/all_users'; import { cleanKibana } from '../../tasks/common'; import { clickInspectButton, closesModal } from '../../tasks/inspect'; -import { loginAndWaitForUsersDetailsPage, loginAndWaitForPage } from '../../tasks/login'; +import { loginAndWaitForPage } from '../../tasks/login'; import { USERS_URL } from '../../urls/navigation'; @@ -31,18 +31,4 @@ describe('Inspect', () => { cy.get(INSPECT_MODAL).should('be.visible'); }); }); - - context('Users details', () => { - before(() => { - loginAndWaitForUsersDetailsPage(); - }); - afterEach(() => { - closesModal(); - }); - - it(`inspects user details all users table`, () => { - clickInspectButton(ALL_USERS_TABLE); - cy.get(INSPECT_MODAL).should('be.visible'); - }); - }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/users/users_anomalies_tab.spec.ts b/x-pack/plugins/security_solution/cypress/integration/users/users_anomalies_tab.spec.ts new file mode 100644 index 0000000000000..bb269e378c8bd --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/users/users_anomalies_tab.spec.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ANOMALIES_TAB, ANOMALIES_TAB_CONTENT } from '../../screens/users/user_anomalies'; +import { cleanKibana } from '../../tasks/common'; + +import { loginAndWaitForPage } from '../../tasks/login'; + +import { USERS_URL } from '../../urls/navigation'; + +describe('Users anomalies tab', () => { + before(() => { + cleanKibana(); + loginAndWaitForPage(USERS_URL); + }); + + it(`renders anomalies tab`, () => { + cy.get(ANOMALIES_TAB).click(); + + cy.get(ANOMALIES_TAB_CONTENT).should('exist'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/integration/users/users_risk_score_tab.spec.ts b/x-pack/plugins/security_solution/cypress/integration/users/users_risk_score_tab.spec.ts new file mode 100644 index 0000000000000..c2d0751d6d112 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/users/users_risk_score_tab.spec.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RISK_SCORE_TAB_CONTENT, RISK_SCORE_TAB } from '../../screens/users/user_risk_score'; +import { cleanKibana } from '../../tasks/common'; + +import { loginAndWaitForPage } from '../../tasks/login'; + +import { USERS_URL } from '../../urls/navigation'; + +describe('Users risk tab', () => { + before(() => { + cleanKibana(); + loginAndWaitForPage(USERS_URL); + }); + + it(`renders users risk tab`, () => { + cy.get(RISK_SCORE_TAB).click(); + + cy.get(RISK_SCORE_TAB_CONTENT).should('exist'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts index 3209200cf25a1..e6e0bcaf1e997 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/host_risk.ts @@ -20,10 +20,9 @@ export const HOST_BY_RISK_TABLE = '.table-hostRisk-loading-false'; export const HOST_BY_RISK_TABLE_CELL = '[data-test-subj="table-hostRisk-loading-false"] .euiTableCellContent'; -export const HOST_BY_RISK_TABLE_FILTER = '[data-test-subj="host-risk-filter-button"]'; +export const HOST_BY_RISK_TABLE_FILTER = '[data-test-subj="risk-filter-button"]'; -export const HOST_BY_RISK_TABLE_FILTER_CRITICAL = - '[data-test-subj="host-risk-filter-item-Critical"]'; +export const HOST_BY_RISK_TABLE_FILTER_CRITICAL = '[data-test-subj="risk-filter-item-Critical"]'; export const HOST_BY_RISK_TABLE_PERPAGE_BUTTON = '[data-test-subj="loadingMoreSizeRowPopover"] button'; diff --git a/x-pack/plugins/security_solution/cypress/screens/users/all_users.ts b/x-pack/plugins/security_solution/cypress/screens/users/all_users.ts index f77c9036970cc..a1d6e9edebf56 100644 --- a/x-pack/plugins/security_solution/cypress/screens/users/all_users.ts +++ b/x-pack/plugins/security_solution/cypress/screens/users/all_users.ts @@ -6,3 +6,7 @@ */ export const ALL_USERS_TABLE = '[data-test-subj="table-authentications-loading-false"]'; + +export const HEADER_SUBTITLE = '[data-test-subj="header-panel-subtitle"]'; + +export const USER_NAME_CELL = '[data-test-subj="render-content-user.name"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/users/user_anomalies.ts b/x-pack/plugins/security_solution/cypress/screens/users/user_anomalies.ts new file mode 100644 index 0000000000000..ce5b44dda19af --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/screens/users/user_anomalies.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const ANOMALIES_TAB = '[data-test-subj="navigation-anomalies"]'; +export const ANOMALIES_TAB_CONTENT = '[data-test-subj="user-anomalies-tab"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/users/user_risk_score.ts b/x-pack/plugins/security_solution/cypress/screens/users/user_risk_score.ts new file mode 100644 index 0000000000000..816334d7fc197 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/screens/users/user_risk_score.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const RISK_SCORE_TAB = '[data-test-subj="navigation-userRisk"]'; +export const RISK_SCORE_TAB_CONTENT = '[data-test-subj="table-userRisk-loading-false"]'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx index dbf73f7a6654d..b429a8a9f234e 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/host_risk_summary.tsx @@ -11,9 +11,9 @@ import { FormattedMessage } from '@kbn/i18n-react'; import * as i18n from './translations'; import { RISKY_HOSTS_DOC_LINK } from '../../../../overview/components/overview_risky_host_links/risky_hosts_disabled_module'; import { EnrichedDataRow, ThreatSummaryPanelHeader } from './threat_summary_view'; -import { HostRisk } from '../../../containers/hosts_risk/types'; -import { HostRiskScore } from '../../../../hosts/components/common/host_risk_score'; -import { HostRiskSeverity } from '../../../../../common/search_strategy'; +import { RiskScore } from '../../severity/common'; +import { RiskSeverity } from '../../../../../common/search_strategy'; +import { HostRisk } from '../../../../risk_score/containers'; const HostRiskSummaryComponent: React.FC<{ hostRisk: HostRisk; @@ -56,10 +56,7 @@ const HostRiskSummaryComponent: React.FC<{ + } /> diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx index 4da090bfa106a..5de2ea5c6235c 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx @@ -30,7 +30,7 @@ import { } from '../../../../../common/search_strategy'; import { HostRiskSummary } from './host_risk_summary'; import { EnrichmentSummary } from './enrichment_summary'; -import { HostRisk } from '../../../containers/hosts_risk/types'; +import { HostRisk } from '../../../../risk_score/containers'; export interface ThreatSummaryDescription { browserField: BrowserField; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 13eadfc53ae42..3eb7bd935a9f8 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -40,7 +40,7 @@ import { EnrichmentRangePicker } from './cti_details/enrichment_range_picker'; import { Reason } from './reason'; import { InvestigationGuideView } from './investigation_guide_view'; import { Overview } from './overview'; -import { HostRisk } from '../../containers/hosts_risk/types'; +import { HostRisk } from '../../../risk_score/containers'; type EventViewTab = EuiTabbedContentTab; diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.tsx index eca259a905af8..ab76c848ce16f 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.tsx @@ -57,6 +57,8 @@ const UserDetailsLinkComponent: React.FC<{ isButton?: boolean; onClick?: (e: SyntheticEvent) => void; }> = ({ children, Component, userName, isButton, onClick, title }) => { + const encodedUserName = encodeURIComponent(userName); + const { formatUrl, search } = useFormatUrl(SecurityPageName.users); const { navigateToApp } = useKibana().services.application; const goToUsersDetails = useCallback( @@ -64,19 +66,19 @@ const UserDetailsLinkComponent: React.FC<{ ev.preventDefault(); navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.users, - path: getUsersDetailsUrl(encodeURIComponent(userName), search), + path: getUsersDetailsUrl(encodedUserName, search), }); }, - [userName, navigateToApp, search] + [encodedUserName, navigateToApp, search] ); return isButton ? ( {children ? children : userName} @@ -84,7 +86,7 @@ const UserDetailsLinkComponent: React.FC<{ {children ? children : userName} diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/anomalies_user_table.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/anomalies_user_table.tsx index f3ee7bb89e4c8..cfc24f1fd2bae 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/anomalies_user_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/anomalies_user_table.tsx @@ -65,7 +65,7 @@ const AnomaliesUserTableComponent: React.FC = ({ return null; } else { return ( - + { const original = jest.requireActual('@elastic/eui'); @@ -23,16 +24,16 @@ jest.mock('@elastic/eui', () => { }; }); -describe('HostRiskScore', () => { +describe('RiskScore', () => { const context = {}; it('renders critical severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(HostRiskSeverity.critical); + expect(container).toHaveTextContent(RiskSeverity.critical); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: euiThemeVars.euiColorDanger }), @@ -43,11 +44,11 @@ describe('HostRiskScore', () => { it('renders hight severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(HostRiskSeverity.high); + expect(container).toHaveTextContent(RiskSeverity.high); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: euiThemeVars.euiColorVis9_behindText }), @@ -58,11 +59,11 @@ describe('HostRiskScore', () => { it('renders moderate severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(HostRiskSeverity.moderate); + expect(container).toHaveTextContent(RiskSeverity.moderate); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: euiThemeVars.euiColorWarning }), @@ -73,11 +74,11 @@ describe('HostRiskScore', () => { it('renders low severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(HostRiskSeverity.low); + expect(container).toHaveTextContent(RiskSeverity.low); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: euiThemeVars.euiColorVis0 }), @@ -88,11 +89,11 @@ describe('HostRiskScore', () => { it('renders unknown severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(HostRiskSeverity.unknown); + expect(container).toHaveTextContent(RiskSeverity.unknown); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: euiThemeVars.euiColorMediumShade }), @@ -103,10 +104,10 @@ describe('HostRiskScore', () => { it("doesn't render background-color when hideBackgroundColor is true", () => { const { queryByTestId } = render( - + ); - expect(queryByTestId('host-risk-score')).toHaveStyleRule('background-color', undefined); + expect(queryByTestId('risk-score')).toHaveStyleRule('background-color', undefined); }); }); diff --git a/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.tsx b/x-pack/plugins/security_solution/public/common/components/severity/common/index.tsx similarity index 62% rename from x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.tsx rename to x-pack/plugins/security_solution/public/common/components/severity/common/index.tsx index 39909b736a612..a8bc7c20f7fcb 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.tsx +++ b/x-pack/plugins/security_solution/public/common/components/severity/common/index.tsx @@ -11,18 +11,19 @@ import { EuiHealth, transparentize } from '@elastic/eui'; import styled, { css } from 'styled-components'; import { euiLightVars } from '@kbn/ui-theme'; -import { HostRiskSeverity } from '../../../../common/search_strategy'; -import { WithHoverActions } from '../../../common/components/with_hover_actions'; -export const HOST_RISK_SEVERITY_COLOUR: { [k in HostRiskSeverity]: string } = { - [HostRiskSeverity.unknown]: euiLightVars.euiColorMediumShade, - [HostRiskSeverity.low]: euiLightVars.euiColorVis0, - [HostRiskSeverity.moderate]: euiLightVars.euiColorWarning, - [HostRiskSeverity.high]: euiLightVars.euiColorVis9_behindText, - [HostRiskSeverity.critical]: euiLightVars.euiColorDanger, +import { WithHoverActions } from '../../with_hover_actions'; +import { RiskSeverity } from '../../../../../common/search_strategy'; + +export const RISK_SEVERITY_COLOUR: { [k in RiskSeverity]: string } = { + [RiskSeverity.unknown]: euiLightVars.euiColorMediumShade, + [RiskSeverity.low]: euiLightVars.euiColorVis0, + [RiskSeverity.moderate]: euiLightVars.euiColorWarning, + [RiskSeverity.high]: euiLightVars.euiColorVis9_behindText, + [RiskSeverity.critical]: euiLightVars.euiColorDanger, }; -const HostRiskBadge = styled.div<{ $severity: HostRiskSeverity; $hideBackgroundColor: boolean }>` +const RiskBadge = styled.div<{ $severity: RiskSeverity; $hideBackgroundColor: boolean }>` ${({ theme, $severity, $hideBackgroundColor }) => css` width: fit-content; padding-right: ${theme.eui.paddingSizes.s}; @@ -39,22 +40,22 @@ const HostRiskBadge = styled.div<{ $severity: HostRiskSeverity; $hideBackgroundC const TooltipContainer = styled.div` padding: ${({ theme }) => theme.eui.paddingSizes.s}; `; -export const HostRiskScore: React.FC<{ - severity: HostRiskSeverity; +export const RiskScore: React.FC<{ + severity: RiskSeverity; hideBackgroundColor?: boolean; toolTipContent?: JSX.Element; }> = ({ severity, hideBackgroundColor = false, toolTipContent }) => { const badge = ( - - + {severity} - + ); if (toolTipContent != null) { diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_badges.tsx b/x-pack/plugins/security_solution/public/common/components/severity/severity_badges.tsx similarity index 74% rename from x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_badges.tsx rename to x-pack/plugins/security_solution/public/common/components/severity/severity_badges.tsx index 655a11a8da421..4a95303a14924 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_badges.tsx +++ b/x-pack/plugins/security_solution/public/common/components/severity/severity_badges.tsx @@ -7,9 +7,9 @@ import { EuiFlexGroup, EuiNotificationBadge, EuiFlexItem } from '@elastic/eui'; import React from 'react'; -import { HOST_RISK_SEVERITY_COLOUR, HostRiskScore } from '../common/host_risk_score'; -import { HostRiskSeverity } from '../../../../common/search_strategy'; -import { SeverityCount } from '../../containers/kpi_hosts/risky_hosts'; +import { RiskSeverity } from '../../../../common/search_strategy'; +import { RiskScore, RISK_SEVERITY_COLOUR } from './common'; +import { SeverityCount } from './types'; export const SeverityBadges: React.FC<{ severityCount: SeverityCount; @@ -22,7 +22,7 @@ export const SeverityBadges: React.FC<{ - {(Object.keys(HOST_RISK_SEVERITY_COLOUR) as HostRiskSeverity[]).map((status) => ( + {(Object.keys(RISK_SEVERITY_COLOUR) as RiskSeverity[]).map((status) => ( @@ -34,11 +34,11 @@ export const SeverityBadges: React.FC<{ SeverityBadges.displayName = 'SeverityBadges'; -const SeverityBadge: React.FC<{ status: HostRiskSeverity; count: number }> = React.memo( +const SeverityBadge: React.FC<{ status: RiskSeverity; count: number }> = React.memo( ({ status, count }) => ( - + diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_bar.tsx b/x-pack/plugins/security_solution/public/common/components/severity/severity_bar.tsx similarity index 76% rename from x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_bar.tsx rename to x-pack/plugins/security_solution/public/common/components/severity/severity_bar.tsx index 9522e84333e30..69e0863ea8e0a 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_bar.tsx +++ b/x-pack/plugins/security_solution/public/common/components/severity/severity_bar.tsx @@ -9,9 +9,9 @@ import styled from 'styled-components'; import { EuiColorPaletteDisplay } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { HostRiskSeverity } from '../../../../common/search_strategy'; -import { HOST_RISK_SEVERITY_COLOUR } from '../common/host_risk_score'; -import { SeverityCount } from '../../containers/kpi_hosts/risky_hosts'; +import { RiskSeverity } from '../../../../common/search_strategy'; +import { RISK_SEVERITY_COLOUR } from './common'; +import { SeverityCount } from './types'; const StyledEuiColorPaletteDisplay = styled(EuiColorPaletteDisplay)` &.risk-score-severity-bar { @@ -33,12 +33,12 @@ export const SeverityBar: React.FC<{ }> = ({ severityCount }) => { const palette = useMemo( () => - (Object.keys(HOST_RISK_SEVERITY_COLOUR) as HostRiskSeverity[]).reduce( - (acc: PalletteArray, status: HostRiskSeverity) => { + (Object.keys(RISK_SEVERITY_COLOUR) as RiskSeverity[]).reduce( + (acc: PalletteArray, status: RiskSeverity) => { const previousStop = acc.length > 0 ? acc[acc.length - 1].stop : 0; const newEntry: PalletteObject = { stop: previousStop + (severityCount[status] || 0), - color: HOST_RISK_SEVERITY_COLOUR[status], + color: RISK_SEVERITY_COLOUR[status], }; acc.push(newEntry); return acc; diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_filter_group.tsx b/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.tsx similarity index 57% rename from x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_filter_group.tsx rename to x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.tsx index 656129aec3e5b..7922aebe07c8f 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/severity_filter_group.tsx +++ b/x-pack/plugins/security_solution/public/common/components/severity/severity_filter_group.tsx @@ -14,28 +14,24 @@ import { FilterChecked, useGeneratedHtmlId, } from '@elastic/eui'; -import { useDispatch } from 'react-redux'; -import { HostRiskSeverity } from '../../../../common/search_strategy'; -import * as i18n from './translations'; -import { hostsActions, hostsModel, hostsSelectors } from '../../store'; -import { SeverityCount } from '../../containers/kpi_hosts/risky_hosts'; -import { HostRiskScore } from '../common/host_risk_score'; -import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; -import { State } from '../../../common/store'; + +import { RiskSeverity } from '../../../../common/search_strategy'; +import { SeverityCount } from './types'; +import { RiskScore } from './common'; interface SeverityItems { - risk: HostRiskSeverity; + risk: RiskSeverity; count: number; checked?: FilterChecked; } export const SeverityFilterGroup: React.FC<{ severityCount: SeverityCount; - type: hostsModel.HostsType; -}> = ({ severityCount, type }) => { + selectedSeverities: RiskSeverity[]; + onSelect: (newSelection: RiskSeverity[]) => void; + title: string; +}> = ({ severityCount, selectedSeverities, onSelect, title }) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const dispatch = useDispatch(); - const onButtonClick = useCallback(() => { setIsPopoverOpen(!isPopoverOpen); }, [isPopoverOpen]); @@ -47,40 +43,29 @@ export const SeverityFilterGroup: React.FC<{ const filterGroupPopoverId = useGeneratedHtmlId({ prefix: 'filterGroupPopover', }); - const getHostRiskScoreFilterQuerySelector = useMemo( - () => hostsSelectors.hostRiskScoreSeverityFilterSelector(), - [] - ); - const severitySelectionRedux = useDeepEqualSelector((state: State) => - getHostRiskScoreFilterQuerySelector(state, type) - ); const items: SeverityItems[] = useMemo(() => { const checked: FilterChecked = 'on'; - return (Object.keys(severityCount) as HostRiskSeverity[]).map((k) => ({ + return (Object.keys(severityCount) as RiskSeverity[]).map((k) => ({ risk: k, count: severityCount[k], - checked: severitySelectionRedux.includes(k) ? checked : undefined, + checked: selectedSeverities.includes(k) ? checked : undefined, })); - }, [severityCount, severitySelectionRedux]); + }, [severityCount, selectedSeverities]); const updateSeverityFilter = useCallback( - (selectedSeverity: HostRiskSeverity) => { - const currentSelection = severitySelectionRedux ?? []; + (selectedSeverity: RiskSeverity) => { + const currentSelection = selectedSeverities ?? []; const newSelection = currentSelection.includes(selectedSeverity) ? currentSelection.filter((s) => s !== selectedSeverity) : [...currentSelection, selectedSeverity]; - dispatch( - hostsActions.updateHostRiskScoreSeverityFilter({ - severitySelection: newSelection, - hostsType: type, - }) - ); + + onSelect(newSelection); }, - [dispatch, severitySelectionRedux, type] + [selectedSeverities, onSelect] ); - const totalActiveHosts = useMemo( + const totalActiveItem = useMemo( () => items.reduce((total, item) => (item.checked === 'on' ? total + item.count : total), 0), [items] ); @@ -88,17 +73,17 @@ export const SeverityFilterGroup: React.FC<{ const button = useMemo( () => ( item.checked === 'on')} iconType="arrowDown" isSelected={isPopoverOpen} - numActiveFilters={totalActiveHosts} + numActiveFilters={totalActiveItem} onClick={onButtonClick} > - {i18n.HOST_RISK} + {title} ), - [isPopoverOpen, items, onButtonClick, totalActiveHosts] + [isPopoverOpen, items, onButtonClick, totalActiveItem, title] ); return ( @@ -113,12 +98,12 @@ export const SeverityFilterGroup: React.FC<{
{items.map((item, index) => ( updateSeverityFilter(item.risk)} > - + ))}
diff --git a/x-pack/plugins/security_solution/public/common/components/severity/types.ts b/x-pack/plugins/security_solution/public/common/components/severity/types.ts new file mode 100644 index 0000000000000..94911ec749a57 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/severity/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RiskSeverity } from '../../../../common/search_strategy'; + +export type SeverityCount = { + [k in RiskSeverity]: number; +}; diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx index 78fd8410817f7..73fba86da653c 100644 --- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx @@ -31,6 +31,7 @@ const AnomaliesQueryTabBodyComponent: React.FC = ({ flowTarget, ip, hostName, + userName, indexNames, }) => { const { jobs } = useInstalledSecurityJobs(); @@ -74,6 +75,7 @@ const AnomaliesQueryTabBodyComponent: React.FC = ({ flowTarget={flowTarget} ip={ip} hostName={hostName} + userName={userName} /> ); diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts index ee3ace3819fd3..97b0f34db9651 100644 --- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts +++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts @@ -35,4 +35,5 @@ export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & { hideHistogramIfEmpty?: boolean; ip?: string; hostName?: string; + userName?: string; }; diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index b65a8ece16c03..7795e76c5fbbb 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -13,7 +13,7 @@ import { NetworkTopTablesFields, NetworkTlsFields, NetworkUsersFields, - HostRiskScoreFields, + RiskScoreFields, } from '../../../common/search_strategy'; import { State } from '../store'; @@ -84,7 +84,7 @@ export const mockGlobalState: State = { hostRisk: { activePage: 0, limit: 10, - sort: { field: HostRiskScoreFields.riskScore, direction: Direction.desc }, + sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, severitySelection: [], }, }, @@ -105,7 +105,7 @@ export const mockGlobalState: State = { hostRisk: { activePage: 0, limit: 10, - sort: { field: HostRiskScoreFields.riskScore, direction: Direction.desc }, + sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, severitySelection: [], }, }, @@ -206,15 +206,19 @@ export const mockGlobalState: State = { // TODO sort: { field: RiskScoreFields.riskScore, direction: Direction.desc }, }, [usersModel.UsersTableType.anomalies]: null, + [usersModel.UsersTableType.risk]: { + activePage: 0, + limit: 10, + sort: { + field: RiskScoreFields.timestamp, + direction: Direction.asc, + }, + severitySelection: [], + }, }, }, details: { queries: { - [usersModel.UsersTableType.allUsers]: { - activePage: 0, - limit: 10, - // TODO sort: { field: HostRulesFields.riskScore, direction: Direction.desc }, - }, [usersModel.UsersTableType.anomalies]: null, }, }, diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx index 8abfbb59965ef..e6008028094ad 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx @@ -26,19 +26,21 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { HostRiskSeverity } from '../../../../common/search_strategy'; + import { RISKY_HOSTS_DOC_LINK } from '../../../overview/components/overview_risky_host_links/risky_hosts_disabled_module'; -import { HostRiskScore } from '../common/host_risk_score'; + import * as i18n from './translations'; import { useOnOpenCloseHandler } from '../../../helper_hooks'; +import { RiskScore } from '../../../common/components/severity/common'; +import { RiskSeverity } from '../../../../common/search_strategy'; const tableColumns: Array> = [ { field: 'classification', name: i18n.INFORMATION_CLASSIFICATION_HEADER, - render: (riskScore?: HostRiskSeverity) => { + render: (riskScore?: RiskSeverity) => { if (riskScore != null) { - return ; + return ; } }, }, @@ -50,15 +52,15 @@ const tableColumns: Array> = [ interface TableItem { range?: string; - classification: HostRiskSeverity; + classification: RiskSeverity; } const tableItems: TableItem[] = [ - { classification: HostRiskSeverity.critical, range: i18n.CRITICAL_RISK_DESCRIPTION }, - { classification: HostRiskSeverity.high, range: '70 - 90 ' }, - { classification: HostRiskSeverity.moderate, range: '40 - 70' }, - { classification: HostRiskSeverity.low, range: '20 - 40' }, - { classification: HostRiskSeverity.unknown, range: i18n.UNKNOWN_RISK_DESCRIPTION }, + { classification: RiskSeverity.critical, range: i18n.CRITICAL_RISK_DESCRIPTION }, + { classification: RiskSeverity.high, range: '70 - 90 ' }, + { classification: RiskSeverity.moderate, range: '40 - 70' }, + { classification: RiskSeverity.low, range: '20 - 40' }, + { classification: RiskSeverity.unknown, range: i18n.UNKNOWN_RISK_DESCRIPTION }, ]; export const HOST_RISK_INFO_BUTTON_CLASS = 'HostRiskInformation__button'; diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/columns.tsx index cab6dd08ef018..cfa2c20ec0604 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/columns.tsx @@ -19,14 +19,14 @@ import { Provider } from '../../../timelines/components/timeline/data_providers/ import { HostRiskScoreColumns } from '.'; import * as i18n from './translations'; -import { HostRiskScore } from '../common/host_risk_score'; -import { HostRiskSeverity } from '../../../../common/search_strategy'; import { HostsTableType } from '../../store/model'; +import { RiskSeverity } from '../../../../common/search_strategy'; +import { RiskScore } from '../../../common/components/severity/common'; export const getHostRiskScoreColumns = ({ dispatchSeverityUpdate, }: { - dispatchSeverityUpdate: (s: HostRiskSeverity) => void; + dispatchSeverityUpdate: (s: RiskSeverity) => void; }): HostRiskScoreColumns => [ { field: 'host.name', @@ -96,7 +96,7 @@ export const getHostRiskScoreColumns = ({ render: (risk) => { if (risk != null) { return ( - dispatchSeverityUpdate(risk)}> {i18n.VIEW_HOSTS_BY_SEVERITY(risk.toLowerCase())} diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx index 9994a03b1e666..e4130eee21909 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/index.tsx @@ -20,17 +20,20 @@ import { hostsActions, hostsModel, hostsSelectors } from '../../store'; import { getHostRiskScoreColumns } from './columns'; import type { HostsRiskScore, - HostRiskScoreItem, - HostRiskScoreSortField, + RiskScoreItem, + RiskScoreSortField, + RiskSeverity, } from '../../../../common/search_strategy'; -import { HostRiskScoreFields, HostRiskSeverity } from '../../../../common/search_strategy'; +import { RiskScoreFields } from '../../../../common/search_strategy'; import { State } from '../../../common/store'; import * as i18n from '../hosts_table/translations'; import * as i18nHosts from './translations'; -import { SeverityBar } from './severity_bar'; -import { SeverityBadges } from './severity_badges'; -import { SeverityFilterGroup } from './severity_filter_group'; -import { SeverityCount } from '../../containers/kpi_hosts/risky_hosts'; + +import { SeverityBadges } from '../../../common/components/severity/severity_badges'; +import { SeverityBar } from '../../../common/components/severity/severity_bar'; +import { SeverityFilterGroup } from '../../../common/components/severity/severity_filter_group'; + +import { SeverityCount } from '../../../common/components/severity/types'; export const rowItems: ItemsPerRow[] = [ { @@ -57,9 +60,9 @@ interface HostRiskScoreTableProps { } export type HostRiskScoreColumns = [ - Columns, - Columns, - Columns + Columns, + Columns, + Columns ]; const HostRiskScoreTableComponent: React.FC = ({ @@ -108,7 +111,7 @@ const HostRiskScoreTableComponent: React.FC = ({ if (newSort.direction !== sort.direction || newSort.field !== sort.field) { dispatch( hostsActions.updateHostRiskScoreSort({ - sort: newSort as HostRiskScoreSortField, + sort: newSort as RiskScoreSortField, hostsType: type, }) ); @@ -118,7 +121,7 @@ const HostRiskScoreTableComponent: React.FC = ({ [dispatch, sort, type] ); const dispatchSeverityUpdate = useCallback( - (s: HostRiskSeverity) => { + (s: RiskSeverity) => { dispatch( hostsActions.updateHostRiskScoreSeverityFilter({ severitySelection: [s], @@ -158,13 +161,41 @@ const HostRiskScoreTableComponent: React.FC = ({
); + + const getHostRiskScoreFilterQuerySelector = useMemo( + () => hostsSelectors.hostRiskScoreSeverityFilterSelector(), + [] + ); + const severitySelectionRedux = useDeepEqualSelector((state: State) => + getHostRiskScoreFilterQuerySelector(state, type) + ); + + const onSelect = useCallback( + (newSelection: RiskSeverity[]) => { + dispatch( + hostsActions.updateHostRiskScoreSeverityFilter({ + severitySelection: newSelection, + hostsType: type, + }) + ); + }, + [dispatch, type] + ); + return ( } + headerFilters={ + + } headerSupplement={risk} headerTitle={headerTitle} headerUnit={i18n.UNIT(totalCount)} diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/translations.ts index 9a1fc5600f528..07628c90bfb76 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/translations.ts +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_score_table/translations.ts @@ -20,6 +20,7 @@ export const HOST_RISK_SCORE = i18n.translate( export const HOST_RISK = i18n.translate('xpack.securitySolution.hostsRiskTable.riskTitle', { defaultMessage: 'Host risk classification', }); + export const HOST_RISK_TOOLTIP = i18n.translate( 'xpack.securitySolution.hostsRiskTable.hostRiskToolTip', { diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.test.tsx index cf0d881b09235..a96ffb577d90c 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.test.tsx @@ -9,9 +9,9 @@ import { render } from '@testing-library/react'; import React from 'react'; import { HostRiskScoreOverTime } from '.'; import { TestProviders } from '../../../common/mock'; -import { useHostRiskScore } from '../../containers/host_risk_score'; +import { useHostRiskScore } from '../../../risk_score/containers'; -jest.mock('../../containers/host_risk_score'); +jest.mock('../../../risk_score/containers'); const useHostRiskScoreMock = useHostRiskScore as jest.Mock; describe('Host Risk Flyout', () => { diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.tsx index 02352430b9658..52a840e857fff 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_score_over_time/index.tsx @@ -27,10 +27,10 @@ import { HeaderSection } from '../../../common/components/header_section'; import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect'; import * as i18n from './translations'; import { PreferenceFormattedDate } from '../../../common/components/formatted_date'; -import { HostRiskScoreQueryId } from '../../../common/containers/hosts_risk/types'; -import { useHostRiskScore } from '../../containers/host_risk_score'; import { useQueryInspector } from '../../../common/components/page/manage_query'; import { HostsComponentsQueryProps } from '../../pages/navigation/types'; +import { buildHostNamesFilter } from '../../../../common/search_strategy/security_solution/risk_score'; +import { HostRiskScoreQueryId, useHostRiskScore } from '../../../risk_score/containers'; export interface HostRiskScoreOverTimeProps extends Pick { @@ -80,7 +80,7 @@ const HostRiskScoreOverTimeComponent: React.FC = ({ const theme = useTheme(); const [loading, { data, refetch, inspect }] = useHostRiskScore({ - hostName, + filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, onlyLatest: false, timerange, }); diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/columns.tsx b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/columns.tsx index d6f7809cca606..88d041775320c 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/columns.tsx @@ -21,13 +21,13 @@ import { DefaultDraggable } from '../../../common/components/draggables'; import { HostsTableColumns } from './'; import * as i18n from './translations'; -import { HostRiskSeverity, Maybe } from '../../../../common/search_strategy'; -import { HostRiskScore } from '../common/host_risk_score'; +import { Maybe, RiskSeverity } from '../../../../common/search_strategy'; import { VIEW_HOSTS_BY_SEVERITY } from '../host_risk_score_table/translations'; +import { RiskScore } from '../../../common/components/severity/common'; export const getHostsColumns = ( showRiskColumn: boolean, - dispatchSeverityUpdate: (s: HostRiskSeverity) => void + dispatchSeverityUpdate: (s: RiskSeverity) => void ): HostsTableColumns => { const columns: HostsTableColumns = [ { @@ -155,10 +155,10 @@ export const getHostsColumns = ( truncateText: false, mobileOptions: { show: true }, sortable: false, - render: (riskScore: HostRiskSeverity) => { + render: (riskScore: RiskSeverity) => { if (riskScore != null) { return ( - dispatchSeverityUpdate(riskScore)}> {VIEW_HOSTS_BY_SEVERITY(riskScore.toLowerCase())} diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.tsx index 2415d83f11fef..01306004844d8 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.tsx @@ -25,9 +25,8 @@ import { HostItem, HostsSortField, HostsFields, - HostRiskSeverity, } from '../../../../common/search_strategy/security_solution/hosts'; -import { Direction } from '../../../../common/search_strategy'; +import { Direction, RiskSeverity } from '../../../../common/search_strategy'; import { HostEcs, OsEcs } from '../../../../common/ecs/host'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import { SecurityPageName } from '../../../../common/constants'; @@ -53,7 +52,7 @@ export type HostsTableColumns = [ Columns, Columns, Columns, - Columns? + Columns? ]; const rowItems: ItemsPerRow[] = [ @@ -135,7 +134,7 @@ const HostsTableComponent: React.FC = ({ const riskyHostsFeatureEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); const dispatchSeverityUpdate = useCallback( - (s: HostRiskSeverity) => { + (s: RiskSeverity) => { dispatch( hostsActions.updateHostRiskScoreSeverityFilter({ severitySelection: [s], diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx index 9660aa059e773..4619b300ca05c 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx @@ -15,7 +15,7 @@ import { HostsKpiProps } from './types'; import { CallOutSwitcher } from '../../../common/components/callouts'; import { RISKY_HOSTS_DOC_LINK } from '../../../overview/components/overview_risky_host_links/risky_hosts_disabled_module'; import * as i18n from './translations'; -import { useHostRiskScore } from '../../containers/host_risk_score'; +import { useHostRiskScore } from '../../../risk_score/containers'; export const HostsKpiComponent = React.memo( ({ filterQuery, from, indexNames, to, setQuery, skip, narrowDateRange }) => { diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.test.tsx index f0e3dcfb69c6e..c4fa134bd88e2 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.test.tsx @@ -11,9 +11,7 @@ import { render } from '@testing-library/react'; import { RiskyHosts } from './'; import { TestProviders } from '../../../../common/mock'; -import { HostsKpiRiskyHostsStrategyResponse } from '../../../../../common/search_strategy'; - -jest.mock('../../../containers/kpi_hosts/risky_hosts'); +import { KpiRiskScoreStrategyResponse } from '../../../../../common/search_strategy'; describe('RiskyHosts', () => { const defaultProps = { @@ -54,9 +52,9 @@ describe('RiskyHosts', () => { }); test('it displays risky hosts quantity returned by the API', () => { - const data: HostsKpiRiskyHostsStrategyResponse = { - rawResponse: {} as HostsKpiRiskyHostsStrategyResponse['rawResponse'], - riskyHosts: { + const data: KpiRiskScoreStrategyResponse = { + rawResponse: {} as KpiRiskScoreStrategyResponse['rawResponse'], + kpiRiskScore: { Critical: 1, High: 1, Unknown: 0, diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.tsx index 498c5223d61ef..d4897702f9407 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.tsx @@ -25,19 +25,16 @@ import { import { HostsKpiBaseComponentLoader } from '../common'; import * as i18n from './translations'; -import { - HostRiskSeverity, - HostsKpiRiskyHostsStrategyResponse, -} from '../../../../../common/search_strategy/security_solution/hosts/kpi/risky_hosts'; - import { useInspectQuery } from '../../../../common/hooks/use_inspect_query'; import { useErrorToast } from '../../../../common/hooks/use_error_toast'; -import { HostRiskScore } from '../../common/host_risk_score'; + import { HostRiskInformationButtonIcon, HOST_RISK_INFO_BUTTON_CLASS, } from '../../host_risk_information'; import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; +import { KpiRiskScoreStrategyResponse, RiskSeverity } from '../../../../../common/search_strategy'; +import { RiskScore } from '../../../../common/components/severity/common'; const QUERY_ID = 'hostsKpiRiskyHostsQuery'; @@ -63,7 +60,7 @@ const RiskScoreContainer = styled(EuiFlexItem)` const RiskyHostsComponent: React.FC<{ error: unknown; loading: boolean; - data?: HostsKpiRiskyHostsStrategyResponse; + data?: KpiRiskScoreStrategyResponse; }> = ({ error, loading, data }) => { useInspectQuery(QUERY_ID, loading, data); useErrorToast(i18n.ERROR_TITLE, error); @@ -72,8 +69,8 @@ const RiskyHostsComponent: React.FC<{ return ; } - const criticalRiskCount = data?.riskyHosts.Critical ?? 0; - const hightlRiskCount = data?.riskyHosts.High ?? 0; + const criticalRiskCount = data?.kpiRiskScore.Critical ?? 0; + const hightlRiskCount = data?.kpiRiskScore.High ?? 0; const totalCount = criticalRiskCount + hightlRiskCount; @@ -118,7 +115,7 @@ const RiskyHostsComponent: React.FC<{ - + @@ -130,7 +127,7 @@ const RiskyHostsComponent: React.FC<{ - + diff --git a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.test.tsx index 8f60ffad7214c..2f3a414344cfc 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.test.tsx @@ -9,9 +9,9 @@ import { render } from '@testing-library/react'; import React from 'react'; import { TopHostScoreContributors } from '.'; import { TestProviders } from '../../../common/mock'; -import { useHostRiskScore } from '../../containers/host_risk_score'; +import { useHostRiskScore } from '../../../risk_score/containers'; -jest.mock('../../containers/host_risk_score'); +jest.mock('../../../risk_score/containers'); const useHostRiskScoreMock = useHostRiskScore as jest.Mock; describe('Host Risk Flyout', () => { diff --git a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.tsx index cd294f250b4cb..042a28edbad4a 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/top_host_score_contributors/index.tsx @@ -19,13 +19,14 @@ import { HeaderSection } from '../../../common/components/header_section'; import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect'; import * as i18n from './translations'; import { Direction } from '../../../../../timelines/common'; -import { HostRiskScoreQueryId } from '../../../common/containers/hosts_risk/types'; -import { HostRiskScoreFields } from '../../../../common/search_strategy'; -import { useHostRiskScore } from '../../containers/host_risk_score'; + +import { buildHostNamesFilter, RiskScoreFields } from '../../../../common/search_strategy'; + import { useQueryInspector } from '../../../common/components/page/manage_query'; import { HostsComponentsQueryProps } from '../../pages/navigation/types'; import { RuleLink } from '../../../detections/pages/detection_engine/rules/all/use_columns'; +import { HostRiskScoreQueryId, useHostRiskScore } from '../../../risk_score/containers'; export interface TopHostScoreContributorsProps extends Pick { @@ -36,7 +37,7 @@ export interface TopHostScoreContributorsProps interface TableItem { rank: number; name: string; - id?: string; // TODO Remove the '?' when the new transform is delivered + id: string; } const columns: Array> = [ @@ -74,13 +75,10 @@ const TopHostScoreContributorsComponent: React.FC [from, to] ); - const sort = useMemo( - () => ({ field: HostRiskScoreFields.timestamp, direction: Direction.desc }), - [] - ); + const sort = useMemo(() => ({ field: RiskScoreFields.timestamp, direction: Direction.desc }), []); const [loading, { data, refetch, inspect }] = useHostRiskScore({ - hostName, + filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, timerange, onlyLatest: false, sort, diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/risky_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/risky_hosts/index.tsx deleted file mode 100644 index 090560842f388..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/risky_hosts/index.tsx +++ /dev/null @@ -1,131 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { Observable } from 'rxjs'; -import { filter } from 'rxjs/operators'; -import { useEffect, useMemo, useState } from 'react'; -import { useObservable, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; -import { createFilter } from '../../../../common/containers/helpers'; - -import { - getHostRiskIndex, - HostRiskSeverity, - HostsKpiQueries, - RequestBasicOptions, -} from '../../../../../common/search_strategy'; - -import { - isCompleteResponse, - isErrorResponse, -} from '../../../../../../../../src/plugins/data/common'; -import type { DataPublicPluginStart } from '../../../../../../../../src/plugins/data/public'; -import type { HostsKpiRiskyHostsStrategyResponse } from '../../../../../common/search_strategy/security_solution/hosts/kpi/risky_hosts'; -import { useKibana } from '../../../../common/lib/kibana'; -import { isIndexNotFoundError } from '../../../../common/utils/exceptions'; -import { ESTermQuery } from '../../../../../common/typed_json'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; - -export type RiskyHostsScoreRequestOptions = RequestBasicOptions; - -type GetHostsRiskScoreProps = RiskyHostsScoreRequestOptions & { - data: DataPublicPluginStart; - signal: AbortSignal; -}; - -const getRiskyHosts = ({ - data, - defaultIndex, - timerange, - signal, - filterQuery, -}: GetHostsRiskScoreProps): Observable => - data.search.search( - { - defaultIndex, - factoryQueryType: HostsKpiQueries.kpiRiskyHosts, - filterQuery: createFilter(filterQuery), - timerange, - }, - { - strategy: 'securitySolutionSearchStrategy', - abortSignal: signal, - } - ); - -const getRiskyHostsComplete = ( - props: GetHostsRiskScoreProps -): Observable => { - return getRiskyHosts(props).pipe( - filter((response) => { - return isErrorResponse(response) || isCompleteResponse(response); - }) - ); -}; - -const getRiskyHostsWithOptionalSignal = withOptionalSignal(getRiskyHostsComplete); - -const useRiskyHostsComplete = () => useObservable(getRiskyHostsWithOptionalSignal); - -interface UseRiskyHostProps { - filterQuery?: string | ESTermQuery; - from: string; - to: string; - skip?: boolean; -} -export type SeverityCount = { - [k in HostRiskSeverity]: number; -}; - -interface RiskScoreKpi { - error: unknown; - isModuleDisabled: boolean; - severityCount: SeverityCount; - loading: boolean; -} - -export const useRiskScoreKpi = ({ - filterQuery, - from, - to, - skip, -}: UseRiskyHostProps): RiskScoreKpi => { - const riskyHostsFeatureEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); - const { error, result, start, loading } = useRiskyHostsComplete(); - const { data, spaces } = useKibana().services; - const isModuleDisabled = !!error && isIndexNotFoundError(error); - const [spaceId, setSpaceId] = useState(); - - useEffect(() => { - if (spaces) { - spaces.getActiveSpace().then((space) => setSpaceId(space.id)); - } - }, [spaces]); - - useEffect(() => { - if (!skip && spaceId && riskyHostsFeatureEnabled) { - start({ - data, - timerange: { to, from, interval: '' }, - filterQuery, - defaultIndex: [getHostRiskIndex(spaceId)], - }); - } - }, [data, spaceId, start, filterQuery, to, from, skip, riskyHostsFeatureEnabled]); - - const severityCount = useMemo( - () => ({ - [HostRiskSeverity.unknown]: 0, - [HostRiskSeverity.low]: 0, - [HostRiskSeverity.moderate]: 0, - [HostRiskSeverity.high]: 0, - [HostRiskSeverity.critical]: 0, - ...(result?.riskyHosts ?? {}), - }), - [result] - ); - return { error, severityCount, loading, isModuleDisabled }; -}; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_score_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_score_tab_body.tsx index f2d7c70ab2f0c..11a422fa0cd3d 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_score_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/host_risk_score_tab_body.tsx @@ -7,15 +7,17 @@ import React, { useMemo } from 'react'; import { noop } from 'lodash/fp'; -import { useHostRiskScore } from '../../containers/host_risk_score'; import { HostsComponentsQueryProps } from './types'; import { manageQuery } from '../../../common/components/page/manage_query'; import { HostRiskScoreTable } from '../../components/host_risk_score_table'; -import { useRiskScoreKpi } from '../../containers/kpi_hosts/risky_hosts'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { hostsModel, hostsSelectors } from '../../store'; import { State } from '../../../common/store'; -import { HostRiskScoreQueryId } from '../../../common/containers/hosts_risk/types'; +import { + HostRiskScoreQueryId, + useHostRiskScore, + useHostRiskScoreKpi, +} from '../../../risk_score/containers'; const HostRiskScoreTableManage = manageQuery(HostRiskScoreTable); @@ -48,10 +50,8 @@ export const HostRiskScoreQueryTabBody = ({ sort, }); - const { severityCount, loading: isKpiLoading } = useRiskScoreKpi({ + const { severityCount, loading: isKpiLoading } = useHostRiskScoreKpi({ filterQuery, - from: startDate, - to: endDate, }); return ( diff --git a/x-pack/plugins/security_solution/public/hosts/store/actions.ts b/x-pack/plugins/security_solution/public/hosts/store/actions.ts index 92029913e6fcc..c9e6360dc8b45 100644 --- a/x-pack/plugins/security_solution/public/hosts/store/actions.ts +++ b/x-pack/plugins/security_solution/public/hosts/store/actions.ts @@ -6,11 +6,8 @@ */ import actionCreatorFactory from 'typescript-fsa'; -import { - HostRiskSeverity, - HostsSortField, - HostRiskScoreSortField, -} from '../../../common/search_strategy/security_solution/hosts'; +import { RiskScoreSortField, RiskSeverity } from '../../../common/search_strategy'; +import { HostsSortField } from '../../../common/search_strategy/security_solution/hosts'; import { HostsTableType, HostsType } from './model'; const actionCreator = actionCreatorFactory('x-pack/security_solution/local/hosts'); @@ -39,11 +36,11 @@ export const updateHostsSort = actionCreator<{ }>('UPDATE_HOSTS_SORT'); export const updateHostRiskScoreSort = actionCreator<{ - sort: HostRiskScoreSortField; + sort: RiskScoreSortField; hostsType: HostsType; }>('UPDATE_HOST_RISK_SCORE_SORT'); export const updateHostRiskScoreSeverityFilter = actionCreator<{ - severitySelection: HostRiskSeverity[]; + severitySelection: RiskSeverity[]; hostsType: HostsType; }>('UPDATE_HOST_RISK_SCORE_SEVERITY'); diff --git a/x-pack/plugins/security_solution/public/hosts/store/helpers.test.ts b/x-pack/plugins/security_solution/public/hosts/store/helpers.test.ts index b9a194ea07fc2..64e4d9088abd7 100644 --- a/x-pack/plugins/security_solution/public/hosts/store/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/hosts/store/helpers.test.ts @@ -8,7 +8,7 @@ import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from '../../common/store/constants'; import { HostsModel, HostsTableType, HostsType } from './model'; import { setHostsQueriesActivePageToZero } from './helpers'; -import { Direction, HostsFields, HostRiskScoreFields } from '../../../common/search_strategy'; +import { Direction, HostsFields, RiskScoreFields } from '../../../common/search_strategy'; export const mockHostsState: HostsModel = { page: { @@ -40,7 +40,7 @@ export const mockHostsState: HostsModel = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: HostRiskScoreFields.riskScore, + field: RiskScoreFields.riskScore, direction: Direction.desc, }, severitySelection: [], @@ -76,7 +76,7 @@ export const mockHostsState: HostsModel = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: HostRiskScoreFields.riskScore, + field: RiskScoreFields.riskScore, direction: Direction.desc, }, severitySelection: [], diff --git a/x-pack/plugins/security_solution/public/hosts/store/helpers.ts b/x-pack/plugins/security_solution/public/hosts/store/helpers.ts index 4d93aa3b0312f..4f09cea7c4f74 100644 --- a/x-pack/plugins/security_solution/public/hosts/store/helpers.ts +++ b/x-pack/plugins/security_solution/public/hosts/store/helpers.ts @@ -5,10 +5,10 @@ * 2.0. */ +import { RiskSeverity } from '../../../common/search_strategy'; import { DEFAULT_TABLE_ACTIVE_PAGE } from '../../common/store/constants'; import { HostsModel, HostsTableType, Queries, HostsType } from './model'; -import { HostRiskSeverity } from '../../../common/search_strategy'; export const setHostPageQueriesActivePageToZero = (state: HostsModel): Queries => ({ ...state.page.queries, @@ -67,7 +67,7 @@ export const setHostsQueriesActivePageToZero = (state: HostsModel, type: HostsTy throw new Error(`HostsType ${type} is unknown`); }; -export const generateSeverityFilter = (severitySelection: HostRiskSeverity[]) => +export const generateSeverityFilter = (severitySelection: RiskSeverity[]) => severitySelection.length > 0 ? [ { diff --git a/x-pack/plugins/security_solution/public/hosts/store/model.ts b/x-pack/plugins/security_solution/public/hosts/store/model.ts index 78e8299cc8dea..090a469c5fb76 100644 --- a/x-pack/plugins/security_solution/public/hosts/store/model.ts +++ b/x-pack/plugins/security_solution/public/hosts/store/model.ts @@ -5,12 +5,12 @@ * 2.0. */ +import { Direction } from '../../../common/search_strategy'; import { - Direction, - HostRiskSeverity, - HostRiskScoreSortField, -} from '../../../common/search_strategy'; -import { HostsFields } from '../../../common/search_strategy/security_solution'; + HostsFields, + RiskScoreSortField, + RiskSeverity, +} from '../../../common/search_strategy/security_solution'; export enum HostsType { page = 'page', @@ -38,8 +38,8 @@ export interface HostsQuery extends BasicQueryPaginated { } export interface HostRiskScoreQuery extends BasicQueryPaginated { - sort: HostRiskScoreSortField; - severitySelection: HostRiskSeverity[]; + sort: RiskScoreSortField; + severitySelection: RiskSeverity[]; } export interface Queries { diff --git a/x-pack/plugins/security_solution/public/hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/hosts/store/reducer.ts index 0922ede935946..f413607b85e1c 100644 --- a/x-pack/plugins/security_solution/public/hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/hosts/store/reducer.ts @@ -6,7 +6,7 @@ */ import { reducerWithInitialState } from 'typescript-fsa-reducers'; -import { Direction, HostsFields, HostRiskScoreFields } from '../../../common/search_strategy'; +import { Direction, HostsFields, RiskScoreFields } from '../../../common/search_strategy'; import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from '../../common/store/constants'; @@ -57,7 +57,7 @@ export const initialHostsState: HostsState = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: HostRiskScoreFields.riskScore, + field: RiskScoreFields.riskScore, direction: Direction.desc, }, severitySelection: [], @@ -93,7 +93,7 @@ export const initialHostsState: HostsState = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, sort: { - field: HostRiskScoreFields.riskScore, + field: RiskScoreFields.riskScore, direction: Direction.desc, }, severitySelection: [], diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx index 82c45c8e5bf7e..28c02c6d8ed4f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx @@ -12,11 +12,11 @@ import '../../../common/mock/match_media'; import { TestProviders } from '../../../common/mock'; import { HostOverview } from './index'; -import { useHostRiskScore } from '../../../hosts/containers/host_risk_score'; import { mockData } from './mock'; import { mockAnomalies } from '../../../common/components/ml/mock'; +import { useHostRiskScore } from '../../../risk_score/containers/all'; -jest.mock('../../../hosts/containers/host_risk_score', () => ({ +jest.mock('../../../risk_score/containers/all', () => ({ useHostRiskScore: jest.fn().mockReturnValue([ true, { diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx index 479580a6bfb95..5c1d6a62df5bc 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx @@ -10,7 +10,12 @@ import { euiLightVars as lightTheme, euiDarkVars as darkTheme } from '@kbn/ui-th import { getOr } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; -import { DocValueFields, HostItem, HostRiskSeverity } from '../../../../common/search_strategy'; +import { + buildHostNamesFilter, + DocValueFields, + HostItem, + RiskSeverity, +} from '../../../../common/search_strategy'; import { DEFAULT_DARK_MODE } from '../../../../common/constants'; import { DescriptionList } from '../../../../common/utility_types'; import { useUiSetting$ } from '../../../common/lib/kibana'; @@ -35,8 +40,8 @@ import { import * as i18n from './translations'; import { EndpointOverview } from './endpoint_overview'; import { OverviewDescriptionList } from '../../../common/components/overview_description_list'; -import { HostRiskScore } from '../../../hosts/components/common/host_risk_score'; -import { useHostRiskScore } from '../../../hosts/containers/host_risk_score'; +import { useHostRiskScore } from '../../../risk_score/containers'; +import { RiskScore } from '../../../common/components/severity/common'; interface HostSummaryProps { contextID?: string; // used to provide unique draggable context when viewing in the side panel @@ -81,7 +86,7 @@ export const HostOverview = React.memo( const userPermissions = hasMlUserPermissions(capabilities); const [darkMode] = useUiSetting$(DEFAULT_DARK_MODE); const [_, { data: hostRisk, isModuleEnabled }] = useHostRiskScore({ - hostName, + filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, }); const getDefaultRenderer = useCallback( @@ -114,10 +119,7 @@ export const HostOverview = React.memo( description: ( <> {hostRiskData ? ( - + ) : ( getEmptyTagValue() )} diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.test.tsx index 8f34a94ea6aa2..575ab0057073e 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.test.tsx @@ -22,11 +22,11 @@ import { } from '../../../common/mock'; import { useRiskyHostsDashboardButtonHref } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'; import { useRiskyHostsDashboardLinks } from '../../containers/overview_risky_host_links/use_risky_hosts_dashboard_links'; -import { useHostRiskScore } from '../../../hosts/containers/host_risk_score'; +import { useHostRiskScore } from '../../../risk_score/containers'; jest.mock('../../../common/lib/kibana'); -jest.mock('../../../hosts/containers/host_risk_score'); +jest.mock('../../../risk_score/containers'); const useHostRiskScoreMock = useHostRiskScore as jest.Mock; jest.mock('../../containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href'); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.tsx index 224e55ecb10b7..dc9c48054a1be 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/index.tsx @@ -9,10 +9,9 @@ import React from 'react'; import { RiskyHostsEnabledModule } from './risky_hosts_enabled_module'; import { RiskyHostsDisabledModule } from './risky_hosts_disabled_module'; -import { useHostRiskScore } from '../../../hosts/containers/host_risk_score'; import { useQueryInspector } from '../../../common/components/page/manage_query'; -import { HostRiskScoreQueryId } from '../../../common/containers/hosts_risk/types'; import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; +import { useHostRiskScore, HostRiskScoreQueryId } from '../../../risk_score/containers'; export interface RiskyHostLinksProps extends Pick { timerange: { to: string; from: string }; } diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.tsx index f508da6c1c991..51be0e1f9fb92 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/risky_hosts_panel_view.tsx @@ -15,7 +15,7 @@ import { Link } from '../link_panel/link'; import * as i18n from './translations'; import { VIEW_DASHBOARD } from '../overview_cti_links/translations'; import { NavigateToHost } from './navigate_to_host'; -import { HostRiskScoreQueryId } from '../../../common/containers/hosts_risk/types'; +import { HostRiskScoreQueryId } from '../../../risk_score/containers'; const columns: Array> = [ { diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx index 0226617725e62..c74f3092dfd5e 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx @@ -27,7 +27,7 @@ import { useCtiDashboardLinks } from '../containers/overview_cti_links'; import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; import { initialUserPrivilegesState } from '../../common/components/user_privileges/user_privileges_context'; import { EndpointPrivileges } from '../../../common/endpoint/types'; -import { useHostRiskScore } from '../../hosts/containers/host_risk_score'; +import { useHostRiskScore } from '../../risk_score/containers'; jest.mock('../../common/lib/kibana'); jest.mock('../../common/containers/source'); @@ -80,7 +80,7 @@ jest.mock('../containers/overview_cti_links/use_ti_integrations'); const useTiIntegrationsMock = useTiIntegrations as jest.Mock; useTiIntegrationsMock.mockReturnValue({}); -jest.mock('../../hosts/containers/host_risk_score'); +jest.mock('../../risk_score/containers'); const useHostRiskScoreMock = useHostRiskScore as jest.Mock; useHostRiskScoreMock.mockReturnValue([false, { data: [], isModuleEnabled: false }]); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/index.tsx b/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx similarity index 64% rename from x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/index.tsx rename to x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx index 516895d49b866..b04d9dd05f283 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/risk_score/containers/all/index.tsx @@ -10,16 +10,17 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { inputsModel } from '../../../common/store'; import { createFilter } from '../../../common/containers/helpers'; import { useKibana } from '../../../common/lib/kibana'; import { - HostsQueries, - HostsRiskScoreStrategyResponse, + RiskScoreStrategyResponse, getHostRiskIndex, HostsRiskScore, - HostRiskScoreSortField, - HostsRiskScoreRequestOptions, + UsersRiskScore, + RiskScoreSortField, + RiskScoreRequestOptions, + RiskQueries, + getUserRiskIndex, } from '../../../../common/search_strategy'; import { ESQuery } from '../../../../common/typed_json'; @@ -31,9 +32,11 @@ import { useTransforms } from '../../../transforms/containers/use_transforms'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import { isIndexNotFoundError } from '../../../common/utils/exceptions'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; +import { inputsModel } from '../../../common/store'; +import { useSpaceId } from '../common'; -export interface HostRiskScoreState { - data?: HostsRiskScore[]; +export interface RiskScoreState { + data?: RiskScoreType; inspect: InspectResponse; isInspected: boolean; refetch: inputsModel.Refetch; @@ -41,48 +44,101 @@ export interface HostRiskScoreState { isModuleEnabled: boolean | undefined; } -interface UseHostRiskScore { - sort?: HostRiskScoreSortField; +interface UseRiskScore { + sort?: RiskScoreSortField; filterQuery?: ESQuery | string; skip?: boolean; timerange?: { to: string; from: string }; - hostName?: string; onlyLatest?: boolean; - pagination?: HostsRiskScoreRequestOptions['pagination']; + pagination?: RiskScoreRequestOptions['pagination']; + featureEnabled: boolean; + defaultIndex: string | undefined; } +type UseHostRiskScore = Omit; + +type UseUserRiskScore = Omit; + const isRecord = (item: unknown): item is Record => typeof item === 'object' && !!item; -export const isHostsRiskScoreHit = (item: Partial): item is HostsRiskScore => +export const isRiskScoreHit = (item: unknown): item is HostsRiskScore | UsersRiskScore => isRecord(item) && - isRecord(item.host) && + (isRecord(item.host) || isRecord(item.user)) && + isRecord(item.risk_stats) && typeof item.risk_stats?.risk_score === 'number' && typeof item.risk === 'string'; export const useHostRiskScore = ({ timerange, - hostName, + onlyLatest, + filterQuery, + sort, + skip = false, + pagination, +}: UseHostRiskScore): [boolean, RiskScoreState] => { + const spaceId = useSpaceId(); + const defaultIndex = spaceId ? getHostRiskIndex(spaceId, onlyLatest) : undefined; + + const riskyHostsFeatureEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); + return useRiskScore({ + timerange, + onlyLatest, + filterQuery, + sort, + skip, + pagination, + featureEnabled: riskyHostsFeatureEnabled, + defaultIndex, + }); +}; + +export const useUserRiskScore = ({ + timerange, + onlyLatest, + filterQuery, + sort, + skip = false, + pagination, +}: UseUserRiskScore): [boolean, RiskScoreState] => { + const spaceId = useSpaceId(); + const defaultIndex = spaceId ? getUserRiskIndex(spaceId, onlyLatest) : undefined; + + const usersFeatureEnabled = useIsExperimentalFeatureEnabled('usersEnabled'); + return useRiskScore({ + timerange, + onlyLatest, + filterQuery, + sort, + skip, + pagination, + featureEnabled: usersFeatureEnabled, + defaultIndex, + }); +}; + +export const useRiskScore = ({ + timerange, onlyLatest = true, filterQuery, sort, skip = false, pagination, -}: UseHostRiskScore): [boolean, HostRiskScoreState] => { + featureEnabled, + defaultIndex, +}: UseRiskScore): [boolean, RiskScoreState] => { const { querySize, cursorStart } = pagination || {}; - const { data, spaces } = useKibana().services; + const { data } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); const searchSubscription = useRef(new Subscription()); - const riskyHostsFeatureEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); - const [loading, setLoading] = useState(riskyHostsFeatureEnabled); - const [riskScoreRequest, setHostRiskScoreRequest] = useState( - null - ); + + const [loading, setLoading] = useState(featureEnabled); + const [riskScoreRequest, setRiskScoreRequest] = useState(null); const { getTransformChangesIfTheyExist } = useTransforms(); const { addError, addWarning } = useAppToasts(); - const [riskScoreResponse, setHostRiskScoreResponse] = useState({ + const [riskScoreResponse, setRiskScoreResponse] = useState>({ data: undefined, inspect: { dsl: [], @@ -95,7 +151,7 @@ export const useHostRiskScore = ({ }); const riskScoreSearch = useCallback( - (request: HostsRiskScoreRequestOptions | null) => { + (request: RiskScoreRequestOptions | null) => { if (request == null || skip) { return; } @@ -105,7 +161,7 @@ export const useHostRiskScore = ({ setLoading(true); searchSubscription.current = data.search - .search(request, { + .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, }) @@ -114,11 +170,11 @@ export const useHostRiskScore = ({ if (isCompleteResponse(response)) { const hits = response?.rawResponse?.hits?.hits; - setHostRiskScoreResponse((prevResponse) => ({ + setRiskScoreResponse((prevResponse) => ({ ...prevResponse, - data: isHostsRiskScoreHit(hits?.[0]?._source) - ? (hits?.map((hit) => hit._source) as HostsRiskScore[]) - : [], + data: isRiskScoreHit(hits?.[0]?._source) + ? (hits?.map((hit) => hit._source) as RiskScoreType) + : ([] as unknown as RiskScoreType), inspect: getInspectResponse(response, prevResponse.inspect), refetch: refetch.current, totalCount: response.totalCount, @@ -135,7 +191,7 @@ export const useHostRiskScore = ({ error: (error) => { setLoading(false); if (isIndexNotFoundError(error)) { - setHostRiskScoreResponse((prevResponse) => + setRiskScoreResponse((prevResponse) => !prevResponse ? prevResponse : { @@ -155,29 +211,22 @@ export const useHostRiskScore = ({ }; searchSubscription.current.unsubscribe(); abortCtrl.current.abort(); - if (riskyHostsFeatureEnabled) { + if (featureEnabled) { asyncSearch(); } refetch.current = asyncSearch; }, - [data.search, addError, addWarning, skip, riskyHostsFeatureEnabled] + [data.search, addError, addWarning, skip, featureEnabled] ); - const [spaceId, setSpaceId] = useState(); - - useEffect(() => { - if (spaces) { - spaces.getActiveSpace().then((space) => setSpaceId(space.id)); - } - }, [spaces]); useEffect(() => { - if (spaceId) { - setHostRiskScoreRequest((prevRequest) => { + if (defaultIndex) { + setRiskScoreRequest((prevRequest) => { const myRequest = { ...(prevRequest ?? {}), - defaultIndex: [getHostRiskIndex(spaceId, onlyLatest)], - factoryQueryType: HostsQueries.hostsRiskScore, + defaultIndex: [defaultIndex], + factoryQueryType: RiskQueries.riskScore, filterQuery: createFilter(filterQuery), pagination: cursorStart !== undefined && querySize !== undefined @@ -186,7 +235,6 @@ export const useHostRiskScore = ({ querySize, } : undefined, - hostNames: hostName ? [hostName] : undefined, timerange: timerange ? { to: timerange.to, from: timerange.from, interval: '' } : undefined, @@ -201,14 +249,13 @@ export const useHostRiskScore = ({ } }, [ filterQuery, - spaceId, onlyLatest, timerange, cursorStart, querySize, sort, - hostName, getTransformChangesIfTheyExist, + defaultIndex, ]); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/translations.ts b/x-pack/plugins/security_solution/public/risk_score/containers/all/translations.ts similarity index 100% rename from x-pack/plugins/security_solution/public/hosts/containers/host_risk_score/translations.ts rename to x-pack/plugins/security_solution/public/risk_score/containers/all/translations.ts diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/common/index.ts b/x-pack/plugins/security_solution/public/risk_score/containers/common/index.ts new file mode 100644 index 0000000000000..1277c08aee5a0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/risk_score/containers/common/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState, useEffect } from 'react'; +import { useKibana } from '../../../common/lib/kibana'; + +export const useSpaceId = () => { + const { spaces } = useKibana().services; + + const [spaceId, setSpaceId] = useState(); + + useEffect(() => { + if (spaces) { + spaces.getActiveSpace().then((space) => setSpaceId(space.id)); + } + }, [spaces]); + return spaceId; +}; diff --git a/x-pack/plugins/security_solution/public/common/containers/hosts_risk/types.ts b/x-pack/plugins/security_solution/public/risk_score/containers/index.ts similarity index 74% rename from x-pack/plugins/security_solution/public/common/containers/hosts_risk/types.ts rename to x-pack/plugins/security_solution/public/risk_score/containers/index.ts index 4227dd68abc8e..089c88aa9be37 100644 --- a/x-pack/plugins/security_solution/public/common/containers/hosts_risk/types.ts +++ b/x-pack/plugins/security_solution/public/risk_score/containers/index.ts @@ -5,7 +5,14 @@ * 2.0. */ -import { HostsRiskScore } from '../../../../common/search_strategy'; +import { HostsRiskScore } from '../../../common/search_strategy/security_solution/risk_score'; + +export * from './all'; +export * from './kpi'; + +export const enum UserRiskScoreQueryId { + USERS_BY_RISK = 'UsersByRisk', +} export const enum HostRiskScoreQueryId { DEFAULT = 'HostRiskScore', diff --git a/x-pack/plugins/security_solution/public/risk_score/containers/kpi/index.tsx b/x-pack/plugins/security_solution/public/risk_score/containers/kpi/index.tsx new file mode 100644 index 0000000000000..bad3799b42ed3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/risk_score/containers/kpi/index.tsx @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Observable } from 'rxjs'; +import { filter } from 'rxjs/operators'; +import { useEffect, useMemo } from 'react'; +import { useObservable, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; +import { createFilter } from '../../../common/containers/helpers'; + +import { + getHostRiskIndex, + getUserRiskIndex, + KpiRiskScoreRequestOptions, + KpiRiskScoreStrategyResponse, + RiskQueries, + RiskScoreAggByFields, + RiskSeverity, +} from '../../../../common/search_strategy'; + +import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/common'; +import type { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public'; + +import { useKibana } from '../../../common/lib/kibana'; +import { isIndexNotFoundError } from '../../../common/utils/exceptions'; +import { ESTermQuery } from '../../../../common/typed_json'; +import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; +import { SeverityCount } from '../../../common/components/severity/types'; +import { useSpaceId } from '../common'; + +type GetHostsRiskScoreProps = KpiRiskScoreRequestOptions & { + data: DataPublicPluginStart; + signal: AbortSignal; +}; + +const getRiskyHosts = ({ + data, + defaultIndex, + signal, + filterQuery, + aggBy, +}: GetHostsRiskScoreProps): Observable => + data.search.search( + { + defaultIndex, + factoryQueryType: RiskQueries.kpiRiskScore, + filterQuery: createFilter(filterQuery), + aggBy, + }, + { + strategy: 'securitySolutionSearchStrategy', + abortSignal: signal, + } + ); + +const getRiskyHostsComplete = ( + props: GetHostsRiskScoreProps +): Observable => { + return getRiskyHosts(props).pipe( + filter((response) => { + return isErrorResponse(response) || isCompleteResponse(response); + }) + ); +}; + +const getRiskyHostsWithOptionalSignal = withOptionalSignal(getRiskyHostsComplete); + +const useRiskyHostsComplete = () => useObservable(getRiskyHostsWithOptionalSignal); + +interface RiskScoreKpi { + error: unknown; + isModuleDisabled: boolean; + severityCount: SeverityCount; + loading: boolean; +} + +type UseHostRiskScoreKpiProps = Omit< + UseRiskScoreKpiProps, + 'defaultIndex' | 'aggBy' | 'featureEnabled' +>; +type UseUserRiskScoreKpiProps = Omit< + UseRiskScoreKpiProps, + 'defaultIndex' | 'aggBy' | 'featureEnabled' +>; + +export const useUserRiskScoreKpi = ({ + filterQuery, + skip, +}: UseUserRiskScoreKpiProps): RiskScoreKpi => { + const spaceId = useSpaceId(); + const defaultIndex = spaceId ? getUserRiskIndex(spaceId) : undefined; + const usersFeatureEnabled = useIsExperimentalFeatureEnabled('usersEnabled'); + + return useRiskScoreKpi({ + filterQuery, + skip, + defaultIndex, + aggBy: 'user.name', + featureEnabled: usersFeatureEnabled, + }); +}; + +export const useHostRiskScoreKpi = ({ + filterQuery, + skip, +}: UseHostRiskScoreKpiProps): RiskScoreKpi => { + const spaceId = useSpaceId(); + const defaultIndex = spaceId ? getHostRiskIndex(spaceId) : undefined; + const riskyHostsFeatureEnabled = useIsExperimentalFeatureEnabled('riskyHostsEnabled'); + + return useRiskScoreKpi({ + filterQuery, + skip, + defaultIndex, + aggBy: 'host.name', + featureEnabled: riskyHostsFeatureEnabled, + }); +}; + +interface UseRiskScoreKpiProps { + filterQuery?: string | ESTermQuery; + skip?: boolean; + defaultIndex: string | undefined; + aggBy: RiskScoreAggByFields; + featureEnabled: boolean; +} + +const useRiskScoreKpi = ({ + filterQuery, + skip, + defaultIndex, + aggBy, + featureEnabled, +}: UseRiskScoreKpiProps): RiskScoreKpi => { + const { error, result, start, loading } = useRiskyHostsComplete(); + const { data } = useKibana().services; + const isModuleDisabled = !!error && isIndexNotFoundError(error); + + useEffect(() => { + if (!skip && defaultIndex && featureEnabled) { + start({ + data, + filterQuery, + defaultIndex: [defaultIndex], + aggBy, + }); + } + }, [data, defaultIndex, start, filterQuery, skip, aggBy, featureEnabled]); + + const severityCount = useMemo( + () => ({ + [RiskSeverity.unknown]: 0, + [RiskSeverity.low]: 0, + [RiskSeverity.moderate]: 0, + [RiskSeverity.high]: 0, + [RiskSeverity.critical]: 0, + ...(result?.kpiRiskScore ?? {}), + }), + [result] + ); + return { error, severityCount, loading, isModuleDisabled }; +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx index c5abcd270c194..37c88cc77d110 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx @@ -23,9 +23,8 @@ import { BrowserFields } from '../../../../common/containers/source'; import { EventDetails } from '../../../../common/components/event_details/event_details'; import { TimelineEventsDetailsItem } from '../../../../../common/search_strategy/timeline'; import * as i18n from './translations'; - import { PreferenceFormattedDate } from '../../../../common/components/formatted_date'; -import { HostRisk } from '../../../../common/containers/hosts_risk/types'; +import { HostRisk } from '../../../../risk_score/containers'; export type HandleOnEventClosed = () => void; interface Props { diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 112b3aaab8687..658d2d166b0d8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -35,8 +35,8 @@ import { ALERT_DETAILS } from './translations'; import { useWithCaseDetailsRefresh } from '../../../../common/components/endpoint/host_isolation/endpoint_host_isolation_cases_context'; import { EventDetailsFooter } from './footer'; import { EntityType } from '../../../../../../timelines/common'; -import { useHostRiskScore } from '../../../../hosts/containers/host_risk_score'; -import { HostRisk } from '../../../../common/containers/hosts_risk/types'; +import { buildHostNamesFilter } from '../../../../../common/search_strategy'; +import { useHostRiskScore, HostRisk } from '../../../../risk_score/containers'; const StyledEuiFlyoutBody = styled(EuiFlyoutBody)` .euiFlyoutBody__overflow { @@ -136,7 +136,7 @@ const EventDetailsPanelComponent: React.FC = ({ ); const [hostRiskLoading, { data, isModuleEnabled }] = useHostRiskScore({ - hostName, + filterQuery: hostName ? buildHostNamesFilter([hostName]) : undefined, pagination: { cursorStart: 0, querySize: 1, diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.test.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.test.tsx new file mode 100644 index 0000000000000..9864dd6b096ec --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.test.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { UserRiskScoreColumns } from '.'; +import { getUserRiskScoreColumns } from './columns'; +import { TestProviders } from '../../../common/mock'; + +describe('getUserRiskScoreColumns', () => { + const defaultProps = { + dispatchSeverityUpdate: jest.fn(), + }; + + test('should have expected fields', () => { + const columns = getUserRiskScoreColumns(defaultProps); + + expect(columns[0].field).toBe('user.name'); + expect(columns[1].field).toBe('risk_stats.risk_score'); + expect(columns[2].field).toBe('risk'); + + columns.forEach((column) => { + expect(column).toHaveProperty('name'); + expect(column).toHaveProperty('render'); + expect(column).toHaveProperty('sortable'); + }); + }); + + test('should render user detail link', () => { + const username = 'test_user_name'; + const columns: UserRiskScoreColumns = getUserRiskScoreColumns(defaultProps); + const usernameColumn = columns[0]; + const renderedColumn = usernameColumn.render!(username, null); + + const { queryByTestId } = render({renderedColumn}); + + expect(queryByTestId('users-link-anchor')).toHaveTextContent(username); + }); + + test('should render user score truncated', () => { + const columns: UserRiskScoreColumns = getUserRiskScoreColumns(defaultProps); + + const riskScore = 10.11111111; + const riskScoreColumn = columns[1]; + const renderedColumn = riskScoreColumn.render!(riskScore, null); + + const { queryByTestId } = render({renderedColumn}); + + expect(queryByTestId('risk-score-truncate')).toHaveTextContent('10.11'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.tsx new file mode 100644 index 0000000000000..c3b26aa1e44d3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/columns.tsx @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiIcon, EuiLink, EuiText, EuiToolTip } from '@elastic/eui'; +import { + DragEffects, + DraggableWrapper, +} from '../../../common/components/drag_and_drop/draggable_wrapper'; +import { escapeDataProviderId } from '../../../common/components/drag_and_drop/helpers'; +import { getEmptyTagValue } from '../../../common/components/empty_value'; + +import { IS_OPERATOR } from '../../../timelines/components/timeline/data_providers/data_provider'; +import { Provider } from '../../../timelines/components/timeline/data_providers/provider'; +import { UserRiskScoreColumns } from '.'; + +import * as i18n from './translations'; +import { RiskScore } from '../../../common/components/severity/common'; +import { RiskSeverity } from '../../../../common/search_strategy'; +import { UserDetailsLink } from '../../../common/components/links'; + +export const getUserRiskScoreColumns = ({ + dispatchSeverityUpdate, +}: { + dispatchSeverityUpdate: (s: RiskSeverity) => void; +}): UserRiskScoreColumns => [ + { + field: 'user.name', + name: i18n.USER_NAME, + truncateText: false, + mobileOptions: { show: true }, + sortable: true, + render: (userName) => { + if (userName != null && userName.length > 0) { + const id = escapeDataProviderId(`user-risk-score-table-userName-${userName}`); + return ( + + snapshot.isDragging ? ( + + + + ) : ( + + ) + } + /> + ); + } + return getEmptyTagValue(); + }, + }, + { + field: 'risk_stats.risk_score', + name: i18n.USER_RISK_SCORE, + truncateText: true, + mobileOptions: { show: true }, + sortable: true, + render: (riskScore) => { + if (riskScore != null) { + return ( + + {riskScore.toFixed(2)} + + ); + } + return getEmptyTagValue(); + }, + }, + { + field: 'risk', + name: ( + + <> + {i18n.USER_RISK} + + + ), + truncateText: false, + mobileOptions: { show: true }, + sortable: true, + render: (risk) => { + if (risk != null) { + return ( + dispatchSeverityUpdate(risk)}> + {i18n.VIEW_USERS_BY_SEVERITY(risk.toLowerCase())} + + } + severity={risk} + /> + ); + } + return getEmptyTagValue(); + }, + }, +]; diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.test.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.test.tsx new file mode 100644 index 0000000000000..3faa96b436de0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.test.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render } from '@testing-library/react'; +import { noop } from 'lodash'; +import React from 'react'; +import { UserRiskScoreTable } from '.'; +import { TestProviders } from '../../../common/mock'; +import { UsersType } from '../../store/model'; + +describe('UserRiskScoreTable', () => { + const username = 'test_user_name'; + const defautProps = { + data: [ + { + '@timestamp': '1641902481', + risk: 'High', + risk_stats: { + rule_risks: [], + risk_score: 71, + }, + user: { + name: username, + }, + }, + ], + id: 'test_id', + isInspect: false, + loading: false, + loadPage: noop, + severityCount: { + Unknown: 0, + Low: 0, + Moderate: 0, + High: 0, + Critical: 0, + }, + totalCount: 0, + type: UsersType.page, + }; + + it('renders', () => { + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId('users-link-anchor')).toHaveTextContent(username); + }); +}); diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx new file mode 100644 index 0000000000000..9f782b7f28662 --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/index.tsx @@ -0,0 +1,228 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo, useCallback } from 'react'; +import { useDispatch } from 'react-redux'; + +import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import { + Columns, + Criteria, + ItemsPerRow, + PaginatedTable, +} from '../../../common/components/paginated_table'; + +import { getUserRiskScoreColumns } from './columns'; + +import * as i18nUsers from '../../pages/translations'; +import * as i18n from './translations'; +import { usersModel, usersSelectors } from '../../store'; +import { + UserRiskScoreFields, + UserRiskScoreItem, +} from '../../../../common/search_strategy/security_solution/users/common'; +import { SeverityCount } from '../../../common/components/severity/types'; +import { SeverityBadges } from '../../../common/components/severity/severity_badges'; +import { SeverityBar } from '../../../common/components/severity/severity_bar'; +import { SeverityFilterGroup } from '../../../common/components/severity/severity_filter_group'; +import { usersActions } from '../../../users/store'; +import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; +import { State } from '../../../common/store'; +import { + RiskScoreSortField, + RiskSeverity, + UsersRiskScore, +} from '../../../../common/search_strategy'; + +export const rowItems: ItemsPerRow[] = [ + { + text: i18n.ROWS_5, + numberOfRow: 5, + }, + { + text: i18n.ROWS_10, + numberOfRow: 10, + }, +]; + +const tableType = usersModel.UsersTableType.risk; + +interface UserRiskScoreTableProps { + data: UsersRiskScore[]; + id: string; + isInspect: boolean; + loading: boolean; + loadPage: (newActivePage: number) => void; + severityCount: SeverityCount; + totalCount: number; + type: usersModel.UsersType; +} + +export type UserRiskScoreColumns = [ + Columns, + Columns, + Columns +]; + +const UserRiskScoreTableComponent: React.FC = ({ + data, + id, + isInspect, + loading, + loadPage, + severityCount, + totalCount, + type, +}) => { + const dispatch = useDispatch(); + + const getUserRiskScoreSelector = useMemo(() => usersSelectors.userRiskScoreSelector(), []); + const { activePage, limit, sort } = useDeepEqualSelector((state: State) => + getUserRiskScoreSelector(state) + ); + const updateLimitPagination = useCallback( + (newLimit) => { + dispatch( + usersActions.updateTableLimit({ + usersType: type, + limit: newLimit, + tableType, + }) + ); + }, + [type, dispatch] + ); + + const updateActivePage = useCallback( + (newPage) => { + dispatch( + usersActions.updateTableActivePage({ + activePage: newPage, + usersType: type, + tableType, + }) + ); + }, + [type, dispatch] + ); + + const onSort = useCallback( + (criteria: Criteria) => { + if (criteria.sort != null) { + const newSort = criteria.sort; + if (newSort.direction !== sort.direction || newSort.field !== sort.field) { + dispatch( + usersActions.updateTableSorting({ + sort: newSort as RiskScoreSortField, + }) + ); + } + } + }, + [dispatch, sort] + ); + const dispatchSeverityUpdate = useCallback( + (s: RiskSeverity) => { + dispatch( + usersActions.updateUserRiskScoreSeverityFilter({ + severitySelection: [s], + }) + ); + }, + [dispatch] + ); + const columns = useMemo( + () => getUserRiskScoreColumns({ dispatchSeverityUpdate }), + [dispatchSeverityUpdate] + ); + + const risk = ( + + + + + + + + + ); + + const headerTitle = ( + + {i18nUsers.NAVIGATION_RISK_TITLE} + + + + + ); + + const getUserRiskScoreFilterQuerySelector = useMemo( + () => usersSelectors.usersRiskScoreSeverityFilterSelector(), + [] + ); + const severitySelectionRedux = useDeepEqualSelector((state: State) => + getUserRiskScoreFilterQuerySelector(state) + ); + + const onSelect = useCallback( + (newSelection: RiskSeverity[]) => { + dispatch( + usersActions.updateUserRiskScoreSeverityFilter({ + severitySelection: newSelection, + }) + ); + }, + [dispatch] + ); + + return ( + + } + headerSupplement={risk} + headerTitle={headerTitle} + headerUnit={i18n.UNIT(totalCount)} + id={id} + isInspect={isInspect} + itemsPerRow={rowItems} + limit={limit} + loading={loading} + loadPage={loadPage} + onChange={onSort} + pageOfItems={data} + showMorePagesIndicator={false} + sorting={sort} + split={true} + stackHeader={true} + totalCount={totalCount} + updateLimitPagination={updateLimitPagination} + updateActivePage={updateActivePage} + /> + ); +}; + +UserRiskScoreTableComponent.displayName = 'UserRiskScoreTableComponent'; + +export const UserRiskScoreTable = React.memo(UserRiskScoreTableComponent); + +UserRiskScoreTable.displayName = 'UserRiskScoreTable'; diff --git a/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/translations.ts b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/translations.ts new file mode 100644 index 0000000000000..c33d45c37c28b --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/components/user_risk_score_table/translations.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; + +export const USER_NAME = i18n.translate('xpack.securitySolution.usersRiskTable.userNameTitle', { + defaultMessage: 'User Name', +}); + +export const USER_RISK_SCORE = i18n.translate( + 'xpack.securitySolution.usersRiskTable.userRiskScoreTitle', + { + defaultMessage: 'User risk score', + } +); + +export const USER_RISK_TOOLTIP = i18n.translate( + 'xpack.securitySolution.usersRiskTable.userRiskToolTip', + { + defaultMessage: + 'User risk classification is determined by user risk score. Users classified as Critical or High are indicated as risky.', + } +); + +export const USER_RISK = i18n.translate('xpack.securitySolution.usersRiskTable.riskTitle', { + defaultMessage: 'User risk classification', +}); + +export const VIEW_USERS_BY_SEVERITY = (severity: string) => + i18n.translate('xpack.securitySolution.usersRiskTable.filteredUsersTitle', { + values: { severity }, + defaultMessage: 'View {severity} risk users', + }); + +export const UNIT = (totalCount: number) => + i18n.translate('xpack.securitySolution.usersTable.unit', { + values: { totalCount }, + defaultMessage: `{totalCount, plural, =1 {user} other {users}}`, + }); + +export const ROWS_5 = i18n.translate('xpack.securitySolution.usersTable.rows', { + values: { numRows: 5 }, + defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}', +}); + +export const ROWS_10 = i18n.translate('xpack.securitySolution.usersTable.rows', { + values: { numRows: 10 }, + defaultMessage: '{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}', +}); + +export const USER_RISK_TABLE_TOOLTIP = i18n.translate( + 'xpack.securitySolution.hostsRiskTable.usersTableTitle', + { + defaultMessage: + 'The user risk table is not affected by the KQL time range. This table shows the latest recorded risk score for each user.', + } +); diff --git a/x-pack/plugins/security_solution/public/users/pages/constants.ts b/x-pack/plugins/security_solution/public/users/pages/constants.ts index 48c2937657721..95c0e361e82d8 100644 --- a/x-pack/plugins/security_solution/public/users/pages/constants.ts +++ b/x-pack/plugins/security_solution/public/users/pages/constants.ts @@ -10,6 +10,6 @@ import { UsersTableType } from '../store/model'; export const usersDetailsPagePath = `${USERS_PATH}/:detailName`; -export const usersTabPath = `${USERS_PATH}/:tabName(${UsersTableType.allUsers}|${UsersTableType.anomalies})`; +export const usersTabPath = `${USERS_PATH}/:tabName(${UsersTableType.allUsers}|${UsersTableType.anomalies}|${UsersTableType.risk})`; -export const usersDetailsTabPath = `${usersDetailsPagePath}/:tabName(${UsersTableType.allUsers})`; +export const usersDetailsTabPath = `${usersDetailsPagePath}/:tabName(${UsersTableType.anomalies})`; diff --git a/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx index dcab2e6dcdacb..966fe067fde88 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx @@ -5,35 +5,76 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { Route, Switch } from 'react-router-dom'; import { UsersTableType } from '../../store/model'; -import { useGlobalTime } from '../../../common/containers/use_global_time'; - +import { AnomaliesUserTable } from '../../../common/components/ml/tables/anomalies_user_table'; import { UsersDetailsTabsProps } from './types'; -import { type } from './utils'; - -import { AllUsersQueryTabBody } from '../navigation'; +import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body'; +import { scoreIntervalToDateTime } from '../../../common/components/ml/score/score_interval_to_datetime'; +import { UpdateDateRange } from '../../../common/components/charts/common'; +import { Anomaly } from '../../../common/components/ml/types'; +import { usersDetailsPagePath } from '../constants'; export const UsersDetailsTabs = React.memo( - ({ docValueFields, filterQuery, indexNames, usersDetailsPagePath }) => { - const { from, to, isInitializing, deleteQuery, setQuery } = useGlobalTime(); + ({ + deleteQuery, + filterQuery, + from, + indexNames, + isInitializing, + setQuery, + to, + type, + setAbsoluteRangeDatePicker, + detailName, + }) => { + const narrowDateRange = useCallback( + (score: Anomaly, interval: string) => { + const fromTo = scoreIntervalToDateTime(score, interval); + setAbsoluteRangeDatePicker({ + id: 'global', + from: fromTo.from, + to: fromTo.to, + }); + }, + [setAbsoluteRangeDatePicker] + ); + + const updateDateRange = useCallback( + ({ x }) => { + if (!x) { + return; + } + const [min, max] = x; + setAbsoluteRangeDatePicker({ + id: 'global', + from: new Date(min).toISOString(), + to: new Date(max).toISOString(), + }); + }, + [setAbsoluteRangeDatePicker] + ); + + const tabProps = { + deleteQuery, + endDate: to, + filterQuery, + indexNames, + skip: isInitializing || filterQuery === undefined, + setQuery, + startDate: from, + type, + narrowDateRange, + updateDateRange, + userName: detailName, + }; return ( - - + + ); diff --git a/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx index ddff8109daf24..47bc406876c22 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/details/nav_tabs.tsx @@ -10,15 +10,15 @@ import { UsersDetailsNavTab } from './types'; import { UsersTableType } from '../../store/model'; import { USERS_PATH } from '../../../../common/constants'; -const getTabsOnUsersDetailsUrl = (hostName: string, tabName: UsersTableType) => - `${USERS_PATH}/${hostName}/${tabName}`; +const getTabsOnUsersDetailsUrl = (userName: string, tabName: UsersTableType) => + `${USERS_PATH}/${userName}/${tabName}`; -export const navTabsUsersDetails = (hostName: string): UsersDetailsNavTab => { +export const navTabsUsersDetails = (userName: string): UsersDetailsNavTab => { return { - [UsersTableType.allUsers]: { - id: UsersTableType.allUsers, - name: i18n.NAVIGATION_ALL_USERS_TITLE, - href: getTabsOnUsersDetailsUrl(hostName, UsersTableType.allUsers), + [UsersTableType.anomalies]: { + id: UsersTableType.anomalies, + name: i18n.NAVIGATION_ANOMALIES_TITLE, + href: getTabsOnUsersDetailsUrl(userName, UsersTableType.anomalies), disabled: false, }, }; diff --git a/x-pack/plugins/security_solution/public/users/pages/details/types.ts b/x-pack/plugins/security_solution/public/users/pages/details/types.ts index 7075c627351dd..69974678bf4d9 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/types.ts +++ b/x-pack/plugins/security_solution/public/users/pages/details/types.ts @@ -44,7 +44,7 @@ export type UsersDetailsComponentProps = UsersDetailsComponentReduxProps & UsersDetailsComponentDispatchProps & UsersQueryProps; -type KeyUsersDetailsNavTab = UsersTableType.allUsers; +type KeyUsersDetailsNavTab = UsersTableType.anomalies; export type UsersDetailsNavTab = Record; diff --git a/x-pack/plugins/security_solution/public/users/pages/details/utils.ts b/x-pack/plugins/security_solution/public/users/pages/details/utils.ts index f490e3e46914e..eb2820c6d4869 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/utils.ts +++ b/x-pack/plugins/security_solution/public/users/pages/details/utils.ts @@ -23,6 +23,7 @@ export const type = usersModel.UsersType.details; const TabNameMappedToI18nKey: Record = { [UsersTableType.allUsers]: i18n.NAVIGATION_ALL_USERS_TITLE, [UsersTableType.anomalies]: i18n.NAVIGATION_ANOMALIES_TITLE, + [UsersTableType.risk]: i18n.NAVIGATION_RISK_TITLE, }; export const getBreadcrumbs = ( diff --git a/x-pack/plugins/security_solution/public/users/pages/index.tsx b/x-pack/plugins/security_solution/public/users/pages/index.tsx index 7f85791e99fba..0b6b103b78176 100644 --- a/x-pack/plugins/security_solution/public/users/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/index.tsx @@ -37,7 +37,7 @@ export const UsersContainer = React.memo(() => ( }) => ( diff --git a/x-pack/plugins/security_solution/public/users/pages/nav_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/nav_tabs.tsx index beffcb879cea0..f991316983f49 100644 --- a/x-pack/plugins/security_solution/public/users/pages/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/nav_tabs.tsx @@ -25,4 +25,10 @@ export const navTabsUsers: UsersNavTab = { href: getTabsOnUsersUrl(UsersTableType.anomalies), disabled: false, }, + [UsersTableType.risk]: { + id: UsersTableType.risk, + name: i18n.NAVIGATION_RISK_TITLE, + href: getTabsOnUsersUrl(UsersTableType.risk), + disabled: false, + }, }; diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/all_users_query_tab_body.tsx b/x-pack/plugins/security_solution/public/users/pages/navigation/all_users_query_tab_body.tsx index dfc245a48f7f4..6c494c9752c4f 100644 --- a/x-pack/plugins/security_solution/public/users/pages/navigation/all_users_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/all_users_query_tab_body.tsx @@ -8,7 +8,7 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { useAuthentications } from '../../../hosts/containers/authentications'; -import { AllUsersQueryProps } from './types'; +import { UsersComponentsQueryProps } from './types'; import { AuthenticationTable } from '../../../hosts/components/authentications_table'; import { manageQuery } from '../../../common/components/page/manage_query'; @@ -25,7 +25,7 @@ export const AllUsersQueryTabBody = ({ type, docValueFields, deleteQuery, -}: AllUsersQueryProps) => { +}: UsersComponentsQueryProps) => { const [ loading, { authentications, totalCount, pageInfo, loadPage, id, inspect, isInspected, refetch }, diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/types.ts b/x-pack/plugins/security_solution/public/users/pages/navigation/types.ts index 53f7c74871084..1e4c28f38450e 100644 --- a/x-pack/plugins/security_solution/public/users/pages/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/types.ts @@ -10,7 +10,7 @@ import { ESTermQuery } from '../../../../common/typed_json'; import { DocValueFields } from '../../../../../timelines/common'; import { NavTab } from '../../../common/components/navigation/types'; -type KeyUsersNavTab = UsersTableType.allUsers | UsersTableType.anomalies; +type KeyUsersNavTab = UsersTableType.allUsers | UsersTableType.anomalies | UsersTableType.risk; export type UsersNavTab = Record; export interface QueryTabBodyProps { @@ -20,7 +20,7 @@ export interface QueryTabBodyProps { filterQuery?: string | ESTermQuery; } -export type AllUsersQueryProps = QueryTabBodyProps & { +export type UsersComponentsQueryProps = QueryTabBodyProps & { deleteQuery?: GlobalTimeArgs['deleteQuery']; docValueFields?: DocValueFields[]; indexNames: string[]; diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_score_tab_body.tsx b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_score_tab_body.tsx new file mode 100644 index 0000000000000..a19e7803cb90f --- /dev/null +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/user_risk_score_tab_body.tsx @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { noop } from 'lodash/fp'; + +import { UsersComponentsQueryProps } from './types'; +import { manageQuery } from '../../../common/components/page/manage_query'; +import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; +import { State } from '../../../common/store'; + +import { UserRiskScoreTable } from '../../components/user_risk_score_table'; +import { usersSelectors } from '../../store'; +import { + UserRiskScoreQueryId, + useUserRiskScore, + useUserRiskScoreKpi, +} from '../../../risk_score/containers'; + +const UserRiskScoreTableManage = manageQuery(UserRiskScoreTable); + +export const UserRiskScoreQueryTabBody = ({ + filterQuery, + skip, + setQuery, + type, + deleteQuery, +}: UsersComponentsQueryProps) => { + const getUserRiskScoreSelector = useMemo(() => usersSelectors.userRiskScoreSelector(), []); + const { activePage, limit, sort } = useDeepEqualSelector((state: State) => + getUserRiskScoreSelector(state) + ); + + const pagination = useMemo( + () => ({ + cursorStart: activePage * limit, + querySize: limit, + }), + [activePage, limit] + ); + + const [loading, { data, totalCount, inspect, isInspected, refetch }] = useUserRiskScore({ + filterQuery, + skip, + pagination, + sort, + }); + + const { severityCount, loading: isKpiLoading } = useUserRiskScoreKpi({ + filterQuery, + }); + + return ( + + ); +}; + +UserRiskScoreQueryTabBody.displayName = 'UserRiskScoreQueryTabBody'; diff --git a/x-pack/plugins/security_solution/public/users/pages/translations.ts b/x-pack/plugins/security_solution/public/users/pages/translations.ts index 4bcfc01e41706..7744ef125ffa2 100644 --- a/x-pack/plugins/security_solution/public/users/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/users/pages/translations.ts @@ -24,3 +24,10 @@ export const NAVIGATION_ANOMALIES_TITLE = i18n.translate( defaultMessage: 'Anomalies', } ); + +export const NAVIGATION_RISK_TITLE = i18n.translate( + 'xpack.securitySolution.users.navigation.riskTitle', + { + defaultMessage: 'Users by risk', + } +); diff --git a/x-pack/plugins/security_solution/public/users/pages/users.tsx b/x-pack/plugins/security_solution/public/users/pages/users.tsx index 2f09f59f76f9b..91cdb5cc1e430 100644 --- a/x-pack/plugins/security_solution/public/users/pages/users.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/users.tsx @@ -10,8 +10,8 @@ import styled from 'styled-components'; import { noop } from 'lodash/fp'; import React, { useCallback, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; +import { useParams } from 'react-router-dom'; import { isTab } from '../../../../timelines/public'; - import { SecurityPageName } from '../../app/types'; import { FiltersGlobal } from '../../common/components/filters_global'; import { HeaderPage } from '../../common/components/header_page'; @@ -24,7 +24,7 @@ import { useGlobalFullScreen } from '../../common/containers/use_full_screen'; import { useGlobalTime } from '../../common/containers/use_global_time'; import { useKibana } from '../../common/lib/kibana'; import { convertToBuildEsQuery } from '../../common/lib/keury'; -import { inputsSelectors } from '../../common/store'; +import { inputsSelectors, State } from '../../common/store'; import { setAbsoluteRangeDatePicker } from '../../common/store/inputs/actions'; import { SpyRoute } from '../../common/utils/route/spy_routes'; @@ -33,7 +33,7 @@ import { OverviewEmpty } from '../../overview/components/overview_empty'; import { UsersTabs } from './users_tabs'; import { navTabsUsers } from './nav_tabs'; import * as i18n from './translations'; -import { usersModel } from '../store'; +import { usersModel, usersSelectors } from '../store'; import { onTimelineTabKeyPressed, resetKeyboardFocus, @@ -44,6 +44,8 @@ import { useInvalidFilterQuery } from '../../common/hooks/use_invalid_filter_que import { UsersKpiComponent } from '../components/kpi_users'; import { UpdateDateRange } from '../../common/components/charts/common'; import { LastEventIndexKey } from '../../../common/search_strategy'; +import { generateSeverityFilter } from '../../hosts/store/helpers'; +import { UsersTableType } from '../store/model'; const ID = 'UsersQueryId'; @@ -68,10 +70,27 @@ const UsersComponent = () => { const query = useDeepEqualSelector(getGlobalQuerySelector); const filters = useDeepEqualSelector(getGlobalFiltersQuerySelector); + const getUsersRiskScoreFilterQuerySelector = useMemo( + () => usersSelectors.usersRiskScoreSeverityFilterSelector(), + [] + ); + const severitySelection = useDeepEqualSelector((state: State) => + getUsersRiskScoreFilterQuerySelector(state) + ); + const { to, from, deleteQuery, setQuery, isInitializing } = useGlobalTime(); const { globalFullScreen } = useGlobalFullScreen(); const { uiSettings } = useKibana().services; - const tabsFilters = filters; + + const { tabName } = useParams<{ tabName: string }>(); + const tabsFilters = React.useMemo(() => { + if (tabName === UsersTableType.risk) { + const severityFilter = generateSeverityFilter(severitySelection); + + return [...severityFilter, ...filters]; + } + return filters; + }, [severitySelection, tabName, filters]); const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); const [filterQuery, kqlError] = useMemo( diff --git a/x-pack/plugins/security_solution/public/users/pages/users_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/users_tabs.tsx index 2db83c5d75aea..50de49d1e4af1 100644 --- a/x-pack/plugins/security_solution/public/users/pages/users_tabs.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/users_tabs.tsx @@ -18,6 +18,8 @@ import { Anomaly } from '../../common/components/ml/types'; import { scoreIntervalToDateTime } from '../../common/components/ml/score/score_interval_to_datetime'; import { UpdateDateRange } from '../../common/components/charts/common'; +import { UserRiskScoreQueryTabBody } from './navigation/user_risk_score_tab_body'; + export const UsersTabs = memo( ({ deleteQuery, @@ -78,6 +80,9 @@ export const UsersTabs = memo( + + + ); } diff --git a/x-pack/plugins/security_solution/public/users/store/actions.ts b/x-pack/plugins/security_solution/public/users/store/actions.ts index ef182b37d0b16..262604f68bdf5 100644 --- a/x-pack/plugins/security_solution/public/users/store/actions.ts +++ b/x-pack/plugins/security_solution/public/users/store/actions.ts @@ -7,15 +7,10 @@ import actionCreatorFactory from 'typescript-fsa'; import { usersModel } from '.'; +import { RiskScoreSortField, RiskSeverity } from '../../../common/search_strategy'; const actionCreator = actionCreatorFactory('x-pack/security_solution/local/users'); -export const updateUsersTable = actionCreator<{ - usersType: usersModel.UsersType; - tableType: usersModel.UsersTableType | usersModel.UsersTableType; - updates: usersModel.TableUpdates; -}>('UPDATE_NETWORK_TABLE'); - export const setUsersTablesActivePageToZero = actionCreator('SET_USERS_TABLES_ACTIVE_PAGE_TO_ZERO'); export const setUsersDetailsTablesActivePageToZero = actionCreator( @@ -33,3 +28,11 @@ export const updateTableActivePage = actionCreator<{ activePage: number; tableType: usersModel.UsersTableType; }>('UPDATE_USERS_ACTIVE_PAGE'); + +export const updateTableSorting = actionCreator<{ + sort: RiskScoreSortField; +}>('UPDATE_USERS_SORTING'); + +export const updateUserRiskScoreSeverityFilter = actionCreator<{ + severitySelection: RiskSeverity[]; +}>('UPDATE_USERS_RISK_SEVERITY_FILTER'); diff --git a/x-pack/plugins/security_solution/public/users/store/model.ts b/x-pack/plugins/security_solution/public/users/store/model.ts index 57d9c4b6c62f2..22630d34d48a8 100644 --- a/x-pack/plugins/security_solution/public/users/store/model.ts +++ b/x-pack/plugins/security_solution/public/users/store/model.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { RiskScoreSortField, RiskSeverity } from '../../../common/search_strategy'; + export enum UsersType { page = 'page', details = 'details', @@ -13,6 +15,7 @@ export enum UsersType { export enum UsersTableType { allUsers = 'allUsers', anomalies = 'anomalies', + risk = 'userRisk', } export type AllUsersTables = UsersTableType; @@ -24,22 +27,29 @@ export interface BasicQueryPaginated { export type AllUsersQuery = BasicQueryPaginated; -export interface TableUpdates { - activePage?: number; - limit?: number; - isPtrIncluded?: boolean; - // sort?: SortField; +export interface UsersRiskScoreQuery extends BasicQueryPaginated { + sort: RiskScoreSortField; // TODO fix it when be is implemented + severitySelection: RiskSeverity[]; } export interface UsersQueries { [UsersTableType.allUsers]: AllUsersQuery; [UsersTableType.anomalies]: null | undefined; + [UsersTableType.risk]: UsersRiskScoreQuery; +} + +export interface UserDetailsQueries { + [UsersTableType.anomalies]: null | undefined; } export interface UsersPageModel { queries: UsersQueries; } +export interface UserDetailsPageModel { + queries: UserDetailsQueries; +} + export interface UsersDetailsQueries { [UsersTableType.allUsers]: AllUsersQuery; } @@ -50,5 +60,5 @@ export interface UsersDetailsModel { export interface UsersModel { [UsersType.page]: UsersPageModel; - [UsersType.details]: UsersPageModel; + [UsersType.details]: UserDetailsPageModel; } diff --git a/x-pack/plugins/security_solution/public/users/store/reducer.ts b/x-pack/plugins/security_solution/public/users/store/reducer.ts index 3f4cd69d7f9e8..26b2e8a225d5a 100644 --- a/x-pack/plugins/security_solution/public/users/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/users/store/reducer.ts @@ -6,18 +6,19 @@ */ import { reducerWithInitialState } from 'typescript-fsa-reducers'; -import { get } from 'lodash/fp'; import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from '../../common/store/constants'; import { setUsersTablesActivePageToZero, - updateUsersTable, updateTableActivePage, updateTableLimit, + updateTableSorting, + updateUserRiskScoreSeverityFilter, } from './actions'; import { setUsersPageQueriesActivePageToZero } from './helpers'; import { UsersTableType, UsersModel } from './model'; -import { HostsTableType } from '../../hosts/store/model'; +import { Direction } from '../../../common/search_strategy/common'; +import { RiskScoreFields } from '../../../common/search_strategy'; export const initialUsersState: UsersModel = { page: { @@ -26,63 +27,82 @@ export const initialUsersState: UsersModel = { activePage: DEFAULT_TABLE_ACTIVE_PAGE, limit: DEFAULT_TABLE_LIMIT, }, - [HostsTableType.anomalies]: null, + [UsersTableType.risk]: { + activePage: DEFAULT_TABLE_ACTIVE_PAGE, + limit: DEFAULT_TABLE_LIMIT, + sort: { + field: RiskScoreFields.riskScore, + direction: Direction.desc, + }, + severitySelection: [], + }, + [UsersTableType.anomalies]: null, }, }, details: { queries: { - [UsersTableType.allUsers]: { - activePage: DEFAULT_TABLE_ACTIVE_PAGE, - limit: DEFAULT_TABLE_LIMIT, - }, - [HostsTableType.anomalies]: null, + [UsersTableType.anomalies]: null, }, }, }; export const usersReducer = reducerWithInitialState(initialUsersState) - .case(updateUsersTable, (state, { usersType, tableType, updates }) => ({ + .case(setUsersTablesActivePageToZero, (state) => ({ ...state, - [usersType]: { - ...state[usersType], + page: { + ...state.page, + queries: setUsersPageQueriesActivePageToZero(state), + }, + })) + .case(updateTableActivePage, (state, { activePage, tableType }) => ({ + ...state, + page: { + ...state.page, queries: { - ...state[usersType].queries, + ...state.page.queries, [tableType]: { - ...get([usersType, 'queries', tableType], state), - ...updates, + ...state.page.queries[tableType], + activePage, }, }, }, })) - .case(setUsersTablesActivePageToZero, (state) => ({ + .case(updateTableLimit, (state, { limit, tableType }) => ({ ...state, page: { ...state.page, - queries: setUsersPageQueriesActivePageToZero(state), + queries: { + ...state.page.queries, + [tableType]: { + ...state.page.queries[tableType], + limit, + }, + }, }, })) - .case(updateTableActivePage, (state, { activePage, usersType, tableType }) => ({ + .case(updateTableSorting, (state, { sort }) => ({ ...state, - [usersType]: { - ...state[usersType], + page: { + ...state.page, queries: { - ...state[usersType].queries, - [tableType]: { - ...state[usersType].queries[tableType], - activePage, + ...state.page.queries, + [UsersTableType.risk]: { + ...state.page.queries[UsersTableType.risk], + sort, }, }, }, })) - .case(updateTableLimit, (state, { limit, usersType, tableType }) => ({ + .case(updateUserRiskScoreSeverityFilter, (state, { severitySelection }) => ({ ...state, - [usersType]: { - ...state[usersType], + page: { + ...state.page, queries: { - ...state[usersType].queries, - [tableType]: { - ...state[usersType].queries[tableType], - limit, + ...state.page.queries, + [UsersTableType.risk]: { + ...state.page.queries[UsersTableType.risk], + severitySelection, + activePage: DEFAULT_TABLE_ACTIVE_PAGE, }, }, }, diff --git a/x-pack/plugins/security_solution/public/users/store/selectors.ts b/x-pack/plugins/security_solution/public/users/store/selectors.ts index 8a706ac06156f..bdeacef2bf774 100644 --- a/x-pack/plugins/security_solution/public/users/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/users/store/selectors.ts @@ -15,3 +15,9 @@ const selectUserPage = (state: State): UsersPageModel => state.users.page; export const allUsersSelector = () => createSelector(selectUserPage, (users) => users.queries[UsersTableType.allUsers]); + +export const userRiskScoreSelector = () => + createSelector(selectUserPage, (users) => users.queries[UsersTableType.risk]); + +export const usersRiskScoreSeverityFilterSelector = () => + createSelector(selectUserPage, (users) => users.queries[UsersTableType.risk].severitySelection); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.test.ts index e1c380fc3fcac..905a63ba48a1f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.test.ts @@ -9,7 +9,7 @@ import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants import { HostsRequestOptions } from '../../../../../../common/search_strategy/security_solution'; import * as buildQuery from './query.all_hosts.dsl'; -import * as buildRiskQuery from '../risk_score/query.hosts_risk.dsl'; +import * as buildRiskQuery from '../../risk_score/all/query.risk_score.dsl'; import { allHosts } from '.'; import { mockOptions, @@ -104,7 +104,7 @@ describe('allHosts search strategy', () => { }); test('should query host risk only for hostNames in the current page', async () => { - const buildHostsRiskQuery = jest.spyOn(buildRiskQuery, 'buildHostsRiskScoreQuery'); + const buildHostsRiskQuery = jest.spyOn(buildRiskQuery, 'buildRiskScoreQuery'); const mockedDeps = mockDeps(); // @ts-expect-error incomplete type mockedDeps.esClient.asCurrentUser.search.mockResponse({ hits: { hits: [] } }); @@ -121,7 +121,7 @@ describe('allHosts search strategy', () => { expect(buildHostsRiskQuery).toHaveBeenCalledWith({ defaultIndex: ['ml_host_risk_score_latest_test-space'], - hostNames: [hostName], + filterQuery: { terms: { 'host.name': [hostName] } }, }); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.ts index 460dfdbb5cb95..d936ffdefb6c2 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/index.ts @@ -14,11 +14,14 @@ import { HostsStrategyResponse, HostsQueries, HostsRequestOptions, - HostsRiskScore, HostsEdges, } from '../../../../../../common/search_strategy/security_solution/hosts'; -import { getHostRiskIndex } from '../../../../../../common/search_strategy'; +import { + getHostRiskIndex, + buildHostNamesFilter, + HostsRiskScore, +} from '../../../../../../common/search_strategy'; import { inspectStringifyObject } from '../../../../../utils/build_query'; import { SecuritySolutionFactory } from '../../types'; @@ -26,10 +29,9 @@ import { buildHostsQuery } from './query.all_hosts.dsl'; import { formatHostEdgesData, HOSTS_FIELDS } from './helpers'; import { IScopedClusterClient } from '../../../../../../../../../src/core/server'; -import { buildHostsRiskScoreQuery } from '../risk_score/query.hosts_risk.dsl'; - import { buildHostsQueryEntities } from './query.all_hosts_entities.dsl'; import { EndpointAppContext } from '../../../../../endpoint/types'; +import { buildRiskScoreQuery } from '../../risk_score/all/query.risk_score.dsl'; export const allHosts: SecuritySolutionFactory = { buildDsl: (options: HostsRequestOptions) => { @@ -117,9 +119,9 @@ async function getHostRiskData( ) { try { const hostRiskResponse = await esClient.asCurrentUser.search( - buildHostsRiskScoreQuery({ + buildRiskScoreQuery({ defaultIndex: [getHostRiskIndex(spaceId)], - hostNames, + filterQuery: buildHostNamesFilter(hostNames), }) ); return hostRiskResponse; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.test.ts index 36add5af0ed25..fda1e1c166ce3 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.test.ts @@ -11,14 +11,12 @@ import { allHosts } from './all'; import { hostDetails } from './details'; import { hostOverview } from './overview'; -import { riskScore } from './risk_score'; import { firstOrLastSeenHost } from './last_first_seen'; import { uncommonProcesses } from './uncommon_processes'; import { authentications, authenticationsEntities } from './authentications'; import { hostsKpiAuthentications, hostsKpiAuthenticationsEntities } from './kpi/authentications'; import { hostsKpiHosts, hostsKpiHostsEntities } from './kpi/hosts'; import { hostsKpiUniqueIps, hostsKpiUniqueIpsEntities } from './kpi/unique_ips'; -import { hostsKpiRiskyHosts } from './kpi/risky_hosts'; jest.mock('./all'); jest.mock('./details'); @@ -29,7 +27,6 @@ jest.mock('./authentications'); jest.mock('./kpi/authentications'); jest.mock('./kpi/hosts'); jest.mock('./kpi/unique_ips'); -jest.mock('./risk_score'); describe('hostsFactory', () => { test('should include correct apis', () => { @@ -41,12 +38,10 @@ describe('hostsFactory', () => { [HostsQueries.uncommonProcesses]: uncommonProcesses, [HostsQueries.authentications]: authentications, [HostsQueries.authenticationsEntities]: authenticationsEntities, - [HostsQueries.hostsRiskScore]: riskScore, [HostsKpiQueries.kpiAuthentications]: hostsKpiAuthentications, [HostsKpiQueries.kpiAuthenticationsEntities]: hostsKpiAuthenticationsEntities, [HostsKpiQueries.kpiHosts]: hostsKpiHosts, [HostsKpiQueries.kpiHostsEntities]: hostsKpiHostsEntities, - [HostsKpiQueries.kpiRiskyHosts]: hostsKpiRiskyHosts, [HostsKpiQueries.kpiUniqueIpsEntities]: hostsKpiUniqueIpsEntities, [HostsKpiQueries.kpiUniqueIps]: hostsKpiUniqueIps, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.ts index f182280667e13..cd95a38ec3092 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.ts @@ -21,8 +21,6 @@ import { authentications, authenticationsEntities } from './authentications'; import { hostsKpiAuthentications, hostsKpiAuthenticationsEntities } from './kpi/authentications'; import { hostsKpiHosts, hostsKpiHostsEntities } from './kpi/hosts'; import { hostsKpiUniqueIps, hostsKpiUniqueIpsEntities } from './kpi/unique_ips'; -import { riskScore } from './risk_score'; -import { hostsKpiRiskyHosts } from './kpi/risky_hosts'; export const hostsFactory: Record< HostsQueries | HostsKpiQueries, @@ -36,12 +34,10 @@ export const hostsFactory: Record< [HostsQueries.uncommonProcesses]: uncommonProcesses, [HostsQueries.authentications]: authentications, [HostsQueries.authenticationsEntities]: authenticationsEntities, - [HostsQueries.hostsRiskScore]: riskScore, [HostsKpiQueries.kpiAuthentications]: hostsKpiAuthentications, [HostsKpiQueries.kpiAuthenticationsEntities]: hostsKpiAuthenticationsEntities, [HostsKpiQueries.kpiHosts]: hostsKpiHosts, [HostsKpiQueries.kpiHostsEntities]: hostsKpiHostsEntities, - [HostsKpiQueries.kpiRiskyHosts]: hostsKpiRiskyHosts, [HostsKpiQueries.kpiUniqueIps]: hostsKpiUniqueIps, [HostsKpiQueries.kpiUniqueIpsEntities]: hostsKpiUniqueIpsEntities, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/index.ts deleted file mode 100644 index f1dde9b66b69f..0000000000000 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/index.ts +++ /dev/null @@ -1,53 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { getOr } from 'lodash/fp'; - -import type { IEsSearchResponse } from '../../../../../../../../../../src/plugins/data/common'; -import type { HostsKpiQueries } from '../../../../../../../common/search_strategy'; - -import type { - HostsKpiRiskyHostsRequestOptions, - HostsKpiRiskyHostsStrategyResponse, - HostRiskSeverity, -} from '../../../../../../../common/search_strategy/security_solution/hosts/kpi/risky_hosts'; -import { inspectStringifyObject } from '../../../../../../utils/build_query'; -import type { SecuritySolutionFactory } from '../../../types'; -import { buildHostsKpiRiskyHostsQuery } from './query.hosts_kpi_risky_hosts.dsl'; - -interface AggBucket { - key: HostRiskSeverity; - doc_count: number; -} - -export const hostsKpiRiskyHosts: SecuritySolutionFactory = { - buildDsl: (options: HostsKpiRiskyHostsRequestOptions) => buildHostsKpiRiskyHostsQuery(options), - parse: async ( - options: HostsKpiRiskyHostsRequestOptions, - response: IEsSearchResponse - ): Promise => { - const inspect = { - dsl: [inspectStringifyObject(buildHostsKpiRiskyHostsQuery(options))], - }; - - const riskBuckets = getOr([], 'aggregations.risk.buckets', response.rawResponse); - - const riskyHosts: Record = riskBuckets.reduce( - (cummulative: Record, bucket: AggBucket) => ({ - ...cummulative, - [bucket.key]: getOr(0, 'unique_hosts.value', bucket), - }), - {} - ); - - return { - ...response, - riskyHosts, - inspect, - }; - }, -}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/index.ts index 5b54c63408d10..2fd9d3f9eff50 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/index.ts @@ -12,6 +12,7 @@ import { hostsFactory } from './hosts'; import { matrixHistogramFactory } from './matrix_histogram'; import { networkFactory } from './network'; import { ctiFactoryTypes } from './cti'; +import { riskScoreFactory } from './risk_score'; export const securitySolutionFactory: Record< FactoryQueryTypes, @@ -21,4 +22,5 @@ export const securitySolutionFactory: Record< ...matrixHistogramFactory, ...networkFactory, ...ctiFactoryTypes, + ...riskScoreFactory, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.ts similarity index 69% rename from x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/index.ts rename to x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.ts index 3ebe7404363ff..c1a1cd93dd618 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.ts @@ -7,30 +7,30 @@ import { SecuritySolutionFactory } from '../../types'; import { - HostsRiskScoreRequestOptions, - HostsQueries, - HostsRiskScoreStrategyResponse, + RiskScoreRequestOptions, + RiskScoreStrategyResponse, + RiskQueries, } from '../../../../../../common/search_strategy'; import type { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; import { inspectStringifyObject } from '../../../../../utils/build_query'; -import { buildHostsRiskScoreQuery } from './query.hosts_risk.dsl'; +import { buildRiskScoreQuery } from './query.risk_score.dsl'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; import { getTotalCount } from '../../cti/event_enrichment/helpers'; -export const riskScore: SecuritySolutionFactory = { - buildDsl: (options: HostsRiskScoreRequestOptions) => { +export const riskScore: SecuritySolutionFactory = { + buildDsl: (options: RiskScoreRequestOptions) => { if (options.pagination && options.pagination.querySize >= DEFAULT_MAX_TABLE_QUERY_SIZE) { throw new Error(`No query size above ${DEFAULT_MAX_TABLE_QUERY_SIZE}`); } - return buildHostsRiskScoreQuery(options); + return buildRiskScoreQuery(options); }, parse: async ( - options: HostsRiskScoreRequestOptions, + options: RiskScoreRequestOptions, response: IEsSearchResponse - ): Promise => { + ): Promise => { const inspect = { - dsl: [inspectStringifyObject(buildHostsRiskScoreQuery(options))], + dsl: [inspectStringifyObject(buildRiskScoreQuery(options))], }; const totalCount = getTotalCount(response.rawResponse.hits.total); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/query.risk_score.dsl.ts similarity index 75% rename from x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts rename to x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/query.risk_score.dsl.ts index 1a4b0df910246..b6e17f6532546 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/query.risk_score.dsl.ts @@ -8,18 +8,16 @@ import { Sort } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { Direction, - HostsRiskScoreRequestOptions, - HostRiskScoreFields, - HostRiskScoreSortField, + RiskScoreRequestOptions, + RiskScoreFields, + RiskScoreSortField, } from '../../../../../../common/search_strategy'; - import { createQueryFilterClauses } from '../../../../../utils/build_query'; export const QUERY_SIZE = 10; -export const buildHostsRiskScoreQuery = ({ +export const buildRiskScoreQuery = ({ timerange, - hostNames, filterQuery, defaultIndex, pagination: { querySize, cursorStart } = { @@ -27,7 +25,7 @@ export const buildHostsRiskScoreQuery = ({ cursorStart: 0, }, sort, -}: HostsRiskScoreRequestOptions) => { +}: RiskScoreRequestOptions) => { const filter = createQueryFilterClauses(filterQuery); if (timerange) { @@ -42,10 +40,6 @@ export const buildHostsRiskScoreQuery = ({ }); } - if (hostNames) { - filter.push({ terms: { 'host.name': hostNames } }); - } - const dslQuery = { index: defaultIndex, allow_no_indices: false, @@ -62,7 +56,7 @@ export const buildHostsRiskScoreQuery = ({ return dslQuery; }; -const getQueryOrder = (sort?: HostRiskScoreSortField): Sort => { +const getQueryOrder = (sort?: RiskScoreSortField): Sort => { if (!sort) { return [ { @@ -71,8 +65,8 @@ const getQueryOrder = (sort?: HostRiskScoreSortField): Sort => { ]; } - if (sort.field === HostRiskScoreFields.risk) { - return [{ [HostRiskScoreFields.riskScore]: sort.direction }]; + if (sort.field === RiskScoreFields.risk) { + return [{ [RiskScoreFields.riskScore]: sort.direction }]; } return [{ [sort.field]: sort.direction }]; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/index.ts new file mode 100644 index 0000000000000..73f022c4e5c46 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FactoryQueryTypes, RiskQueries } from '../../../../../common/search_strategy'; +import { SecuritySolutionFactory } from '../types'; +import { riskScore } from './all'; +import { kpiRiskScore } from './kpi'; + +export const riskScoreFactory: Record> = { + [RiskQueries.riskScore]: riskScore, + [RiskQueries.kpiRiskScore]: kpiRiskScore, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/__mocks__/index.ts similarity index 66% rename from x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/__mocks__/index.ts rename to x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/__mocks__/index.ts index c0522d61e3804..89723ab180fcf 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/__mocks__/index.ts @@ -6,11 +6,11 @@ */ import { - HostsKpiQueries, - HostsKpiRiskyHostsRequestOptions, -} from '../../../../../../../../common/search_strategy'; + KpiRiskScoreRequestOptions, + RiskQueries, +} from '../../../../../../../common/search_strategy'; -export const mockOptions: HostsKpiRiskyHostsRequestOptions = { +export const mockOptions: KpiRiskScoreRequestOptions = { defaultIndex: [ 'apm-*-transaction*', 'traces-apm*', @@ -21,8 +21,8 @@ export const mockOptions: HostsKpiRiskyHostsRequestOptions = { 'packetbeat-*', 'winlogbeat-*', ], - factoryQueryType: HostsKpiQueries.kpiRiskyHosts, + factoryQueryType: RiskQueries.kpiRiskScore, filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"filter":[{"bool":{"should":[{"exists":{"field":"host.name"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}', - timerange: { interval: '12h', from: '2020-09-07T09:47:28.606Z', to: '2020-09-08T09:47:28.606Z' }, + aggBy: 'host.name', }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/index.test.ts similarity index 50% rename from x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/index.test.ts rename to x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/index.test.ts index cbfe63d86ea73..f14fadb54e696 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/index.test.ts @@ -5,17 +5,18 @@ * 2.0. */ -import { hostsKpiRiskyHosts } from '.'; -import * as buildQuery from './query.hosts_kpi_risky_hosts.dsl'; +import { kpiRiskScore } from '.'; +import * as buildQuery from './query.kpi_risk_score.dsl'; + import { mockOptions } from './__mocks__'; -describe('buildHostsKpiRiskyHostsQuery search strategy', () => { - const buildHostsKpiRiskyHostsQuery = jest.spyOn(buildQuery, 'buildHostsKpiRiskyHostsQuery'); +describe('buildKpiRiskScoreQuery search strategy', () => { + const buildKpiRiskScoreQuery = jest.spyOn(buildQuery, 'buildKpiRiskScoreQuery'); describe('buildDsl', () => { test('should build dsl query', () => { - hostsKpiRiskyHosts.buildDsl(mockOptions); - expect(buildHostsKpiRiskyHostsQuery).toHaveBeenCalledWith(mockOptions); + kpiRiskScore.buildDsl(mockOptions); + expect(buildKpiRiskScoreQuery).toHaveBeenCalledWith(mockOptions); }); }); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/index.ts new file mode 100644 index 0000000000000..088e16a5edbd5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/index.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getOr } from 'lodash/fp'; + +import type { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; +import type { + KpiRiskScoreRequestOptions, + KpiRiskScoreStrategyResponse, + RiskQueries, +} from '../../../../../../common/search_strategy'; +import { RiskSeverity } from '../../../../../../common/search_strategy'; + +import { inspectStringifyObject } from '../../../../../utils/build_query'; +import type { SecuritySolutionFactory } from '../../types'; +import { buildKpiRiskScoreQuery } from './query.kpi_risk_score.dsl'; + +interface AggBucket { + key: RiskSeverity; + doc_count: number; +} + +export const kpiRiskScore: SecuritySolutionFactory = { + buildDsl: (options: KpiRiskScoreRequestOptions) => buildKpiRiskScoreQuery(options), + parse: async ( + options: KpiRiskScoreRequestOptions, + response: IEsSearchResponse + ): Promise => { + const inspect = { + dsl: [inspectStringifyObject(buildKpiRiskScoreQuery(options))], + }; + + const riskBuckets = getOr([], 'aggregations.risk.buckets', response.rawResponse); + + const result: Record = riskBuckets.reduce( + (cummulative: Record, bucket: AggBucket) => ({ + ...cummulative, + [bucket.key]: getOr(0, 'unique_entries.value', bucket), + }), + {} + ); + + return { + ...response, + kpiRiskScore: result, + inspect, + }; + }, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/query.hosts_kpi_risky_hosts.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/query.kpi_risk_score.dsl.ts similarity index 68% rename from x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/query.hosts_kpi_risky_hosts.dsl.ts rename to x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/query.kpi_risk_score.dsl.ts index 83c3d3b2d34f7..259f64af2592a 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/risky_hosts/query.hosts_kpi_risky_hosts.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/kpi/query.kpi_risk_score.dsl.ts @@ -5,13 +5,14 @@ * 2.0. */ -import type { HostsKpiRiskyHostsRequestOptions } from '../../../../../../../common/search_strategy/security_solution/hosts/kpi/risky_hosts'; -import { createQueryFilterClauses } from '../../../../../../utils/build_query'; +import { KpiRiskScoreRequestOptions } from '../../../../../../common/search_strategy'; +import { createQueryFilterClauses } from '../../../../../utils/build_query'; -export const buildHostsKpiRiskyHostsQuery = ({ +export const buildKpiRiskScoreQuery = ({ defaultIndex, filterQuery, -}: HostsKpiRiskyHostsRequestOptions) => { + aggBy, +}: KpiRiskScoreRequestOptions) => { const filter = [...createQueryFilterClauses(filterQuery)]; const dslQuery = { @@ -26,9 +27,9 @@ export const buildHostsKpiRiskyHostsQuery = ({ field: 'risk.keyword', }, aggs: { - unique_hosts: { + unique_entries: { cardinality: { - field: 'host.name', + field: aggBy, }, }, }, From d7c26c114bac0f09c90e80715281301cd24169bf Mon Sep 17 00:00:00 2001 From: CohenIdo <90558359+CohenIdo@users.noreply.github.com> Date: Tue, 8 Mar 2022 14:27:54 +0200 Subject: [PATCH 054/140] create rules config based on rules status and update package policy (#126988) --- .../common/constants.ts | 1 + .../common/schemas/csp_configuration.ts | 15 ++ .../routes/benchmarks/benchmarks.test.ts | 16 +- .../server/routes/benchmarks/benchmarks.ts | 5 +- .../update_rules_configuration.test.ts | 160 ++++++++++++++++++ .../update_rules_configuration.ts | 155 +++++++++++++++++ .../server/routes/index.ts | 2 + 7 files changed, 338 insertions(+), 16 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/common/schemas/csp_configuration.ts create mode 100644 x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts create mode 100644 x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 84c62e62c3351..351b21c8a636d 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -8,6 +8,7 @@ export const STATS_ROUTE_PATH = '/api/csp/stats'; export const FINDINGS_ROUTE_PATH = '/api/csp/findings'; export const BENCHMARKS_ROUTE_PATH = '/api/csp/benchmarks'; +export const UPDATE_RULES_CONFIG_ROUTE_PATH = '/api/csp/update_rules_config'; export const CSP_KUBEBEAT_INDEX_PATTERN = 'logs-cis_kubernetes_benchmark.findings*'; export const AGENT_LOGS_INDEX_PATTERN = '.logs-cis_kubernetes_benchmark.metadata*'; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_configuration.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_configuration.ts new file mode 100644 index 0000000000000..f5d38e938e2cc --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/common/schemas/csp_configuration.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { schema as rt, TypeOf } from '@kbn/config-schema'; + +export const cspRulesConfigSchema = rt.object({ + activated_rules: rt.object({ + cis_k8s: rt.arrayOf(rt.string()), + }), +}); + +export type CspRulesConfigSchema = TypeOf; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts index 8c9d04dc207f3..7b3093be6801c 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts @@ -109,25 +109,15 @@ describe('benchmarks API', () => { }); describe('test getPackagePolicies', () => { - it('should throw when agentPolicyService is undefined', async () => { - const mockAgentPolicyService = undefined; - expect( - getPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { - page: 1, - per_page: 100, - }) - ).rejects.toThrow(); - }); - it('should format request by package name', async () => { - const mockAgentPolicyService = createPackagePolicyServiceMock(); + const mockPackagePolicyService = createPackagePolicyServiceMock(); - await getPackagePolicies(mockSoClient, mockAgentPolicyService, 'myPackage', { + await getPackagePolicies(mockSoClient, mockPackagePolicyService, 'myPackage', { page: 1, per_page: 100, }); - expect(mockAgentPolicyService.list.mock.calls[0][1]).toMatchObject( + expect(mockPackagePolicyService.list.mock.calls[0][1]).toMatchObject( expect.objectContaining({ kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:myPackage`, page: 1, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts index c52aeead6cd4d..7d09b87de85d4 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts @@ -54,7 +54,7 @@ const getPackageNameQuery = (packageName: string, benchmarkFilter?: string): str export const getPackagePolicies = async ( soClient: SavedObjectsClientContract, - packagePolicyService: PackagePolicyServiceInterface | undefined, + packagePolicyService: PackagePolicyServiceInterface, packageName: string, queryParams: BenchmarksQuerySchema ): Promise => { @@ -159,8 +159,7 @@ export const defineGetBenchmarksRoute = (router: IRouter, cspContext: CspAppCont const agentPolicyService = cspContext.service.agentPolicyService; const packagePolicyService = cspContext.service.packagePolicyService; - // TODO: This validate can be remove after #2819 will be merged - if (!agentPolicyService || !agentService) { + if (!agentPolicyService || !agentService || !packagePolicyService) { throw new Error(`Failed to get Fleet services`); } diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts new file mode 100644 index 0000000000000..4e534d565d7e3 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; +import { savedObjectsClientMock, httpServiceMock, loggingSystemMock } from 'src/core/server/mocks'; +import { + convertRulesConfigToYaml, + createRulesConfig, + defineUpdateRulesConfigRoute, + getCspRules, + setVarToPackagePolicy, + updatePackagePolicy, +} from './update_rules_configuration'; + +import { CspAppService } from '../../lib/csp_app_services'; +import { CspAppContext } from '../../plugin'; +import { createPackagePolicyMock } from '../../../../fleet/common/mocks'; +import { createPackagePolicyServiceMock } from '../../../../fleet/server/mocks'; + +import { cspRuleAssetSavedObjectType, CspRuleSchema } from '../../../common/schemas/csp_rule'; +import { + ElasticsearchClient, + SavedObjectsClientContract, + SavedObjectsFindResponse, +} from 'kibana/server'; +import { Chance } from 'chance'; + +describe('Update rules configuration API', () => { + let logger: ReturnType; + let mockEsClient: jest.Mocked; + let mockSoClient: jest.Mocked; + const chance = new Chance(); + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + jest.clearAllMocks(); + }); + + it('validate the API route path', async () => { + const router = httpServiceMock.createRouter(); + const cspAppContextService = new CspAppService(); + + const cspContext: CspAppContext = { + logger, + service: cspAppContextService, + }; + defineUpdateRulesConfigRoute(router, cspContext); + + const [config, _] = router.post.mock.calls[0]; + + expect(config.path).toEqual('/api/csp/update_rules_config'); + }); + + it('validate getCspRules input parameters', async () => { + mockSoClient = savedObjectsClientMock.create(); + mockSoClient.find.mockResolvedValueOnce({} as SavedObjectsFindResponse); + await getCspRules(mockSoClient); + expect(mockSoClient.find).toBeCalledTimes(1); + expect(mockSoClient.find).toHaveBeenCalledWith( + expect.objectContaining({ type: cspRuleAssetSavedObjectType }) + ); + }); + + it('create csp rules config based on activated csp rules', async () => { + const cspRules = { + page: 1, + per_page: 1000, + total: 2, + saved_objects: [ + { + type: 'csp_rule', + id: '1.1.1', + attributes: { enabled: true }, + }, + { + type: 'csp_rule', + id: '1.1.2', + attributes: { enabled: false }, + }, + { + type: 'csp_rule', + id: '1.1.3', + attributes: { enabled: true }, + }, + ], + } as SavedObjectsFindResponse; + const cspConfig = await createRulesConfig(cspRules); + expect(cspConfig).toMatchObject({ activated_rules: { cis_k8s: ['1.1.1', '1.1.3'] } }); + }); + + it('create empty csp rules config when all rules are disabled', async () => { + const cspRules = { + page: 1, + per_page: 1000, + total: 2, + saved_objects: [ + { + type: 'csp_rule', + id: '1.1.1', + attributes: { enabled: false }, + }, + { + type: 'csp_rule', + id: '1.1.2', + attributes: { enabled: false }, + }, + ], + } as SavedObjectsFindResponse; + const cspConfig = await createRulesConfig(cspRules); + expect(cspConfig).toMatchObject({ activated_rules: { cis_k8s: [] } }); + }); + + it('validate converting rules config object to Yaml', async () => { + const cspRuleConfig = { activated_rules: { cis_k8s: ['1.1.1', '1.1.2'] } }; + + const dataYaml = convertRulesConfigToYaml(cspRuleConfig); + + expect(dataYaml).toEqual('activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n'); + }); + + it('validate adding new data.yaml to package policy instance', async () => { + const packagePolicy = createPackagePolicyMock(); + + const dataYaml = 'activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n'; + const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, dataYaml); + + expect(updatedPackagePolicy.vars).toEqual({ dataYaml: { type: 'config', value: dataYaml } }); + }); + + it('validate updatePackagePolicy is called with the right parameters', async () => { + mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + mockSoClient = savedObjectsClientMock.create(); + const mockPackagePolicyService = createPackagePolicyServiceMock(); + + const packagePolicyId1 = chance.guid(); + const packagePolicyId2 = chance.guid(); + const mockPackagePolicy1 = createPackagePolicyMock(); + const mockPackagePolicy2 = createPackagePolicyMock(); + mockPackagePolicy1.id = packagePolicyId1; + mockPackagePolicy2.id = packagePolicyId2; + const packagePolicies = mockPackagePolicy1; + + const dataYaml = 'activated_rules:\n cis_k8s:\n - 1.1.1\n - 1.1.2\n'; + + await updatePackagePolicy( + mockPackagePolicyService, + packagePolicies, + mockEsClient, + mockSoClient, + dataYaml + ); + + expect(mockPackagePolicyService.update).toBeCalledTimes(1); + expect(mockPackagePolicyService.update.mock.calls[0][2]).toEqual(packagePolicyId1); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts new file mode 100644 index 0000000000000..50a4759c5ec52 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { + ElasticsearchClient, + IRouter, + SavedObjectsClientContract, + SavedObjectsFindResponse, +} from 'src/core/server'; +import { schema as rt } from '@kbn/config-schema'; +import { transformError } from '@kbn/securitysolution-es-utils'; + +import { produce } from 'immer'; +import { unset } from 'lodash'; +import yaml from 'js-yaml'; + +import { PackagePolicy, PackagePolicyConfigRecord } from '../../../../fleet/common'; +import { CspAppContext } from '../../plugin'; +import { CspRulesConfigSchema } from '../../../common/schemas/csp_configuration'; +import { CspRuleSchema, cspRuleAssetSavedObjectType } from '../../../common/schemas/csp_rule'; +import { UPDATE_RULES_CONFIG_ROUTE_PATH } from '../../../common/constants'; +import { CIS_KUBERNETES_PACKAGE_NAME } from '../../../common/constants'; +import { PackagePolicyServiceInterface } from '../../../../fleet/server'; + +export const getPackagePolicy = async ( + soClient: SavedObjectsClientContract, + packagePolicyService: PackagePolicyServiceInterface, + packagePolicyId: string +): Promise => { + const packagePolicies = await packagePolicyService.getByIDs(soClient, [packagePolicyId]); + + // PackagePolicies always contains one element, even when package does not exist + if (!packagePolicies || !packagePolicies[0].version) { + throw new Error(`package policy Id '${packagePolicyId}' is not exist`); + } + if (packagePolicies[0].package?.name !== CIS_KUBERNETES_PACKAGE_NAME) { + // TODO: improve this validator to support any future CSP package + throw new Error(`Package Policy Id '${packagePolicyId}' is not CSP package`); + } + + return packagePolicies![0]; +}; + +export const getCspRules = async (soClient: SavedObjectsClientContract) => { + const cspRules = await soClient.find({ + type: cspRuleAssetSavedObjectType, + search: '', + searchFields: ['name'], + // TODO: research how to get all rules + perPage: 10000, + }); + return cspRules; +}; + +export const createRulesConfig = ( + cspRules: SavedObjectsFindResponse +): CspRulesConfigSchema => { + const activatedRules = cspRules.saved_objects.filter((cspRule) => cspRule.attributes.enabled); + + const config = { + activated_rules: { + cis_k8s: activatedRules.map((activatedRule) => activatedRule.id), + }, + }; + return config; +}; + +export const convertRulesConfigToYaml = (config: CspRulesConfigSchema): string => { + return yaml.safeDump(config); +}; + +export const setVarToPackagePolicy = ( + packagePolicy: PackagePolicy, + dataYaml: string +): PackagePolicy => { + const configFile: PackagePolicyConfigRecord = { + dataYaml: { type: 'config', value: dataYaml }, + }; + const updatedPackagePolicy = produce(packagePolicy, (draft) => { + unset(draft, 'id'); + draft.vars = configFile; + // TODO: disable comments after adding base config to integration + // draft.inputs[0].vars = configFile; + }); + return updatedPackagePolicy; +}; + +export const updatePackagePolicy = ( + packagePolicyService: PackagePolicyServiceInterface, + packagePolicy: PackagePolicy, + esClient: ElasticsearchClient, + soClient: SavedObjectsClientContract, + dataYaml: string +): Promise => { + const updatedPackagePolicy = setVarToPackagePolicy(packagePolicy, dataYaml); + return packagePolicyService.update(soClient, esClient, packagePolicy.id, updatedPackagePolicy); +}; + +export const defineUpdateRulesConfigRoute = (router: IRouter, cspContext: CspAppContext): void => + router.post( + { + path: UPDATE_RULES_CONFIG_ROUTE_PATH, + validate: { query: configurationUpdateInputSchema }, + }, + async (context, request, response) => { + try { + const esClient = context.core.elasticsearch.client.asCurrentUser; + const soClient = context.core.savedObjects.client; + const packagePolicyService = cspContext.service.packagePolicyService; + const packagePolicyId = request.query.package_policy_id; + + if (!packagePolicyService) { + throw new Error(`Failed to get Fleet services`); + } + const packagePolicy = await getPackagePolicy( + soClient, + packagePolicyService, + packagePolicyId + ); + + const cspRules = await getCspRules(soClient); + const rulesConfig = createRulesConfig(cspRules); + const dataYaml = convertRulesConfigToYaml(rulesConfig); + + const updatedPackagePolicies = await updatePackagePolicy( + packagePolicyService!, + packagePolicy, + esClient, + soClient, + dataYaml + ); + + return response.ok({ body: updatedPackagePolicies }); + } catch (err) { + const error = transformError(err); + cspContext.logger.error( + `Failed to update rules configuration on package policy ${error.message}` + ); + return response.customError({ + body: { message: error.message }, + statusCode: error.statusCode, + }); + } + } + ); + +export const configurationUpdateInputSchema = rt.object({ + /** + * CSP integration instance ID + */ + package_policy_id: rt.string(), +}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/index.ts b/x-pack/plugins/cloud_security_posture/server/routes/index.ts index c0b333e4058aa..aa04a610aa486 100755 --- a/x-pack/plugins/cloud_security_posture/server/routes/index.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/index.ts @@ -9,10 +9,12 @@ import type { IRouter } from '../../../../../src/core/server'; import { defineGetComplianceDashboardRoute } from './compliance_dashboard/compliance_dashboard'; import { defineGetBenchmarksRoute } from './benchmarks/benchmarks'; import { defineFindingsIndexRoute as defineGetFindingsIndexRoute } from './findings/findings'; +import { defineUpdateRulesConfigRoute } from './configuration/update_rules_configuration'; import { CspAppContext } from '../plugin'; export function defineRoutes(router: IRouter, cspContext: CspAppContext) { defineGetComplianceDashboardRoute(router, cspContext); defineGetFindingsIndexRoute(router, cspContext); defineGetBenchmarksRoute(router, cspContext); + defineUpdateRulesConfigRoute(router, cspContext); } From a5f410ecf7cf1c5cdf012ee3e1f797fb44d78ba2 Mon Sep 17 00:00:00 2001 From: Sandra G Date: Tue, 8 Mar 2022 08:13:58 -0500 Subject: [PATCH 055/140] [Stack Monitoring] add back api integration tests and update esArchiver data (#126998) * comment tests back in for es, kibana, cluster, logstash * comment tests back in for APM and update data to ECS .monitoring-beats-8-mb * add beats integration tests and update archive data Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apis/monitoring/apm/index.js | 6 +- .../apis/monitoring/apm/instance_mb.js | 7 +- .../apis/monitoring/apm/instances_mb.js | 11 +- .../apis/monitoring/apm/overview_mb.js | 7 +- .../apis/monitoring/beats/detail_mb.js | 7 +- .../apis/monitoring/beats/index.js | 6 +- .../apis/monitoring/beats/list_mb.js | 11 +- .../apis/monitoring/beats/overview_mb.js | 7 +- .../apis/monitoring/cluster/index.js | 4 +- .../apis/monitoring/elasticsearch/index.js | 16 +- .../apis/monitoring/kibana/index.js | 6 +- .../apis/monitoring/logstash/index.js | 10 +- .../monitoring/apm_mb/data.json.gz | Bin 4314 -> 5108 bytes .../monitoring/apm_mb/mappings.json | 22921 --------------- .../monitoring/beats_mb/data.json.gz | Bin 553234 -> 643153 bytes .../monitoring/beats_mb/mappings.json | 23623 --------------- .../data.json.gz | Bin 170648 -> 187819 bytes .../mappings.json | 23689 ---------------- 18 files changed, 52 insertions(+), 70279 deletions(-) delete mode 100644 x-pack/test/functional/es_archives/monitoring/apm_mb/mappings.json delete mode 100644 x-pack/test/functional/es_archives/monitoring/beats_mb/mappings.json delete mode 100644 x-pack/test/functional/es_archives/monitoring/beats_with_restarted_instance_mb/mappings.json diff --git a/x-pack/test/api_integration/apis/monitoring/apm/index.js b/x-pack/test/api_integration/apis/monitoring/apm/index.js index b56a26d71a83a..ae0623054dc6b 100644 --- a/x-pack/test/api_integration/apis/monitoring/apm/index.js +++ b/x-pack/test/api_integration/apis/monitoring/apm/index.js @@ -8,10 +8,10 @@ export default function ({ loadTestFile }) { describe('APM', () => { loadTestFile(require.resolve('./overview')); - // loadTestFile(require.resolve('./overview_mb')); + loadTestFile(require.resolve('./overview_mb')); loadTestFile(require.resolve('./instances')); - // loadTestFile(require.resolve('./instances_mb')); + loadTestFile(require.resolve('./instances_mb')); loadTestFile(require.resolve('./instance')); - // loadTestFile(require.resolve('./instance_mb')); + loadTestFile(require.resolve('./instance_mb')); }); } diff --git a/x-pack/test/api_integration/apis/monitoring/apm/instance_mb.js b/x-pack/test/api_integration/apis/monitoring/apm/instance_mb.js index 38179368a8764..37e17fce25b64 100644 --- a/x-pack/test/api_integration/apis/monitoring/apm/instance_mb.js +++ b/x-pack/test/api_integration/apis/monitoring/apm/instance_mb.js @@ -7,12 +7,13 @@ import expect from '@kbn/expect'; import apmInstanceFixture from './fixtures/instance'; +import { getLifecycleMethods } from '../data_stream'; export default function ({ getService }) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); describe('instance detail mb', () => { + const { setup, tearDown } = getLifecycleMethods(getService); const archive = 'x-pack/test/functional/es_archives/monitoring/apm_mb'; const timeRange = { min: '2018-08-31T12:59:49.104Z', @@ -20,11 +21,11 @@ export default function ({ getService }) { }; before('load archive', () => { - return esArchiver.load(archive); + return setup(archive); }); after('unload archive', () => { - return esArchiver.unload(archive); + return tearDown(); }); it('should get apm instance data', async () => { diff --git a/x-pack/test/api_integration/apis/monitoring/apm/instances_mb.js b/x-pack/test/api_integration/apis/monitoring/apm/instances_mb.js index 9248ba32350cb..33fc109715215 100644 --- a/x-pack/test/api_integration/apis/monitoring/apm/instances_mb.js +++ b/x-pack/test/api_integration/apis/monitoring/apm/instances_mb.js @@ -6,24 +6,25 @@ */ import expect from '@kbn/expect'; +import { getLifecycleMethods } from '../data_stream'; export default function ({ getService }) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); describe('list mb', () => { + const { setup, tearDown } = getLifecycleMethods(getService); const archive = 'x-pack/test/functional/es_archives/monitoring/apm_mb'; const timeRange = { min: '2018-08-31T12:59:49.104Z', max: '2018-08-31T13:59:49.104Z', }; - before('load clusters archive', () => { - return esArchiver.load(archive); + before('load archive', () => { + return setup(archive); }); - after('unload clusters archive', () => { - return esArchiver.unload(archive); + after('unload archive', () => { + return tearDown(); }); it('should load multiple clusters', async () => { diff --git a/x-pack/test/api_integration/apis/monitoring/apm/overview_mb.js b/x-pack/test/api_integration/apis/monitoring/apm/overview_mb.js index 734b493caeec5..8aef96ab1d9e6 100644 --- a/x-pack/test/api_integration/apis/monitoring/apm/overview_mb.js +++ b/x-pack/test/api_integration/apis/monitoring/apm/overview_mb.js @@ -7,14 +7,15 @@ import expect from '@kbn/expect'; import apmClusterFixture from './fixtures/cluster'; +import { getLifecycleMethods } from '../data_stream'; export default function ({ getService }) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); describe('overview mb', function () { // Archive contains non-cgroup data which collides with the in-cgroup APM server present by default on cloud deployments this.tags(['skipCloud']); + const { setup, tearDown } = getLifecycleMethods(getService); const archive = 'x-pack/test/functional/es_archives/monitoring/apm_mb'; const timeRange = { @@ -23,11 +24,11 @@ export default function ({ getService }) { }; before('load archive', () => { - return esArchiver.load(archive); + return setup(archive); }); after('unload archive', () => { - return esArchiver.unload(archive); + return tearDown(); }); it('should summarize apm cluster with metrics', async () => { diff --git a/x-pack/test/api_integration/apis/monitoring/beats/detail_mb.js b/x-pack/test/api_integration/apis/monitoring/beats/detail_mb.js index 745b317612b53..a7f6ebda90feb 100644 --- a/x-pack/test/api_integration/apis/monitoring/beats/detail_mb.js +++ b/x-pack/test/api_integration/apis/monitoring/beats/detail_mb.js @@ -7,12 +7,13 @@ import expect from '@kbn/expect'; import beatDetailFixture from './fixtures/detail'; +import { getLifecycleMethods } from '../data_stream'; export default function ({ getService }) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); describe('instance detail mb', () => { + const { setup, tearDown } = getLifecycleMethods(getService); const archive = 'x-pack/test/functional/es_archives/monitoring/beats_with_restarted_instance_mb'; const timeRange = { @@ -21,11 +22,11 @@ export default function ({ getService }) { }; before('load archive', () => { - return esArchiver.load(archive); + return setup(archive); }); after('unload archive', () => { - return esArchiver.unload(archive); + return tearDown(); }); it('should summarize beat with metrics', async () => { diff --git a/x-pack/test/api_integration/apis/monitoring/beats/index.js b/x-pack/test/api_integration/apis/monitoring/beats/index.js index 235afdc7fe6b2..8fe09e457f685 100644 --- a/x-pack/test/api_integration/apis/monitoring/beats/index.js +++ b/x-pack/test/api_integration/apis/monitoring/beats/index.js @@ -8,10 +8,10 @@ export default function ({ loadTestFile }) { describe('Beats', () => { loadTestFile(require.resolve('./overview')); - // loadTestFile(require.resolve('./overview_mb')); + loadTestFile(require.resolve('./overview_mb')); loadTestFile(require.resolve('./list')); - // loadTestFile(require.resolve('./list_mb')); + loadTestFile(require.resolve('./list_mb')); loadTestFile(require.resolve('./detail')); - // loadTestFile(require.resolve('./detail_mb')); + loadTestFile(require.resolve('./detail_mb')); }); } diff --git a/x-pack/test/api_integration/apis/monitoring/beats/list_mb.js b/x-pack/test/api_integration/apis/monitoring/beats/list_mb.js index 767ba107d0822..fdc0184d9e8e4 100644 --- a/x-pack/test/api_integration/apis/monitoring/beats/list_mb.js +++ b/x-pack/test/api_integration/apis/monitoring/beats/list_mb.js @@ -6,12 +6,13 @@ */ import expect from '@kbn/expect'; +import { getLifecycleMethods } from '../data_stream'; export default function ({ getService }) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); describe('list mb', () => { + const { setup, tearDown } = getLifecycleMethods(getService); describe('with restarted beat instance', () => { const archive = 'x-pack/test/functional/es_archives/monitoring/beats_with_restarted_instance_mb'; @@ -20,12 +21,12 @@ export default function ({ getService }) { max: '2018-02-09T21:50:00Z', }; - before('load clusters archive', () => { - return esArchiver.load(archive); + before('load archive', () => { + return setup(archive); }); - after('unload clusters archive', () => { - return esArchiver.unload(archive); + after('unload archive', () => { + return tearDown(); }); it('should load multiple clusters', async () => { diff --git a/x-pack/test/api_integration/apis/monitoring/beats/overview_mb.js b/x-pack/test/api_integration/apis/monitoring/beats/overview_mb.js index 6a20309e226a0..c168443f3e778 100644 --- a/x-pack/test/api_integration/apis/monitoring/beats/overview_mb.js +++ b/x-pack/test/api_integration/apis/monitoring/beats/overview_mb.js @@ -7,12 +7,13 @@ import expect from '@kbn/expect'; import beatsClusterFixture from './fixtures/cluster'; +import { getLifecycleMethods } from '../data_stream'; export default function ({ getService }) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); describe('overview mb', () => { + const { setup, tearDown } = getLifecycleMethods(getService); const archive = 'x-pack/test/functional/es_archives/monitoring/beats_mb'; const timeRange = { min: '2017-12-19T18:11:32.000Z', @@ -20,11 +21,11 @@ export default function ({ getService }) { }; before('load archive', () => { - return esArchiver.load(archive); + return setup(archive); }); after('unload archive', () => { - return esArchiver.unload(archive); + return tearDown(); }); it('should summarize beats cluster with metrics', async () => { diff --git a/x-pack/test/api_integration/apis/monitoring/cluster/index.js b/x-pack/test/api_integration/apis/monitoring/cluster/index.js index 59af14b049aa7..fb99f5e0b9605 100644 --- a/x-pack/test/api_integration/apis/monitoring/cluster/index.js +++ b/x-pack/test/api_integration/apis/monitoring/cluster/index.js @@ -8,8 +8,8 @@ export default function ({ loadTestFile }) { describe('cluster', () => { loadTestFile(require.resolve('./list')); - // loadTestFile(require.resolve('./list_mb')); + loadTestFile(require.resolve('./list_mb')); loadTestFile(require.resolve('./overview')); - // loadTestFile(require.resolve('./overview_mb')); + loadTestFile(require.resolve('./overview_mb')); }); } diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/index.js b/x-pack/test/api_integration/apis/monitoring/elasticsearch/index.js index 167da3e360c16..ad864979a15fe 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/index.js +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/index.js @@ -8,20 +8,20 @@ export default function ({ loadTestFile }) { describe('Elasticsearch', () => { loadTestFile(require.resolve('./overview')); - // loadTestFile(require.resolve('./overview_mb')); + loadTestFile(require.resolve('./overview_mb')); loadTestFile(require.resolve('./nodes')); - // loadTestFile(require.resolve('./nodes_mb')); + loadTestFile(require.resolve('./nodes_mb')); loadTestFile(require.resolve('./node_detail')); - // loadTestFile(require.resolve('./node_detail_mb')); + loadTestFile(require.resolve('./node_detail_mb')); loadTestFile(require.resolve('./node_detail_advanced')); - // loadTestFile(require.resolve('./node_detail_advanced_mb')); + loadTestFile(require.resolve('./node_detail_advanced_mb')); loadTestFile(require.resolve('./indices')); - // loadTestFile(require.resolve('./indices_mb')); + loadTestFile(require.resolve('./indices_mb')); loadTestFile(require.resolve('./index_detail')); - // loadTestFile(require.resolve('./index_detail_mb')); + loadTestFile(require.resolve('./index_detail_mb')); loadTestFile(require.resolve('./ccr')); - // loadTestFile(require.resolve('./ccr_mb')); + loadTestFile(require.resolve('./ccr_mb')); loadTestFile(require.resolve('./ccr_shard')); - // loadTestFile(require.resolve('./ccr_shard_mb')); + loadTestFile(require.resolve('./ccr_shard_mb')); }); } diff --git a/x-pack/test/api_integration/apis/monitoring/kibana/index.js b/x-pack/test/api_integration/apis/monitoring/kibana/index.js index f953104d915e8..b54b09102bc1b 100644 --- a/x-pack/test/api_integration/apis/monitoring/kibana/index.js +++ b/x-pack/test/api_integration/apis/monitoring/kibana/index.js @@ -8,10 +8,10 @@ export default function ({ loadTestFile }) { describe('Kibana', () => { loadTestFile(require.resolve('./overview')); - // loadTestFile(require.resolve('./overview_mb')); + loadTestFile(require.resolve('./overview_mb')); loadTestFile(require.resolve('./listing')); - // loadTestFile(require.resolve('./listing_mb')); + loadTestFile(require.resolve('./listing_mb')); loadTestFile(require.resolve('./instance')); - // loadTestFile(require.resolve('./instance_mb')); + loadTestFile(require.resolve('./instance_mb')); }); } diff --git a/x-pack/test/api_integration/apis/monitoring/logstash/index.js b/x-pack/test/api_integration/apis/monitoring/logstash/index.js index 1a8baacacf066..0caf7e360ef3a 100644 --- a/x-pack/test/api_integration/apis/monitoring/logstash/index.js +++ b/x-pack/test/api_integration/apis/monitoring/logstash/index.js @@ -8,14 +8,14 @@ export default function ({ loadTestFile }) { describe('Logstash', () => { loadTestFile(require.resolve('./overview')); - // loadTestFile(require.resolve('./overview_mb')); + loadTestFile(require.resolve('./overview_mb')); loadTestFile(require.resolve('./nodes')); - // loadTestFile(require.resolve('./nodes_mb')); + loadTestFile(require.resolve('./nodes_mb')); loadTestFile(require.resolve('./node_detail')); - // loadTestFile(require.resolve('./node_detail_mb')); + loadTestFile(require.resolve('./node_detail_mb')); loadTestFile(require.resolve('./multicluster_pipelines')); - // loadTestFile(require.resolve('./multicluster_pipelines_mb')); + loadTestFile(require.resolve('./multicluster_pipelines_mb')); loadTestFile(require.resolve('./pipelines')); - // loadTestFile(require.resolve('./pipelines_mb')); + loadTestFile(require.resolve('./pipelines_mb')); }); } diff --git a/x-pack/test/functional/es_archives/monitoring/apm_mb/data.json.gz b/x-pack/test/functional/es_archives/monitoring/apm_mb/data.json.gz index 3065596f55b5d88182711ea7fe1a29e3a3a4ef14..226e2f8f76f66bef3369f980871518f8d6ec8b35 100644 GIT binary patch literal 5108 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 Oh-A(v*?xT?wBdi;3hf2} diff --git a/x-pack/test/functional/es_archives/monitoring/apm_mb/mappings.json b/x-pack/test/functional/es_archives/monitoring/apm_mb/mappings.json deleted file mode 100644 index 4e6af879b1fe5..0000000000000 --- a/x-pack/test/functional/es_archives/monitoring/apm_mb/mappings.json +++ /dev/null @@ -1,22921 +0,0 @@ -{ - "type": "index", - "value": { - "index": "metricbeat-8.0.0", - "mappings": { - "_meta": { - "beat": "metricbeat", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "docker.cpu.core.*.pct": { - "mapping": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "path_match": "docker.cpu.core.*.pct" - } - }, - { - "docker.cpu.core.*.norm.pct": { - "mapping": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "path_match": "docker.cpu.core.*.norm.pct" - } - }, - { - "docker.cpu.core.*.ticks": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "docker.cpu.core.*.ticks" - } - }, - { - "docker.event.actor.attributes": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.event.actor.attributes.*" - } - }, - { - "docker.image.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.image.labels.*" - } - }, - { - "docker.memory.stats.*": { - "mapping": { - "type": "long" - }, - "path_match": "docker.memory.stats.*" - } - }, - { - "etcd.disk.wal_fsync_duration.ns.bucket.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "etcd.disk.wal_fsync_duration.ns.bucket.*" - } - }, - { - "etcd.disk.backend_commit_duration.ns.bucket.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "etcd.disk.backend_commit_duration.ns.bucket.*" - } - }, - { - "kubernetes.apiserver.http.request.duration.us.percentile.*": { - "mapping": { - "type": "double" - }, - "match_mapping_type": "double", - "path_match": "kubernetes.apiserver.http.request.duration.us.percentile.*" - } - }, - { - "kubernetes.apiserver.http.request.size.bytes.percentile.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.apiserver.http.request.size.bytes.percentile.*" - } - }, - { - "kubernetes.apiserver.http.response.size.bytes.percentile.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.apiserver.http.response.size.bytes.percentile.*" - } - }, - { - "kubernetes.apiserver.request.latency.bucket.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.apiserver.request.latency.bucket.*" - } - }, - { - "kubernetes.apiserver.request.duration.us.bucket.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.apiserver.request.duration.us.bucket.*" - } - }, - { - "kubernetes.controllermanager.http.request.duration.us.percentile.*": { - "mapping": { - "type": "double" - }, - "match_mapping_type": "double", - "path_match": "kubernetes.controllermanager.http.request.duration.us.percentile.*" - } - }, - { - "kubernetes.controllermanager.http.request.size.bytes.percentile.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.controllermanager.http.request.size.bytes.percentile.*" - } - }, - { - "kubernetes.controllermanager.http.response.size.bytes.percentile.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.controllermanager.http.response.size.bytes.percentile.*" - } - }, - { - "kubernetes.proxy.http.request.duration.us.percentile.*": { - "mapping": { - "type": "double" - }, - "match_mapping_type": "double", - "path_match": "kubernetes.proxy.http.request.duration.us.percentile.*" - } - }, - { - "kubernetes.proxy.http.request.size.bytes.percentile.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.proxy.http.request.size.bytes.percentile.*" - } - }, - { - "kubernetes.proxy.http.response.size.bytes.percentile.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.proxy.http.response.size.bytes.percentile.*" - } - }, - { - "kubernetes.proxy.sync.rules.duration.us.bucket.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.proxy.sync.rules.duration.us.bucket.*" - } - }, - { - "kubernetes.proxy.sync.networkprogramming.duration.us.bucket.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.proxy.sync.networkprogramming.duration.us.bucket.*" - } - }, - { - "kubernetes.scheduler.http.request.duration.us.percentile.*": { - "mapping": { - "type": "double" - }, - "match_mapping_type": "double", - "path_match": "kubernetes.scheduler.http.request.duration.us.percentile.*" - } - }, - { - "kubernetes.scheduler.http.request.size.bytes.percentile.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.scheduler.http.request.size.bytes.percentile.*" - } - }, - { - "kubernetes.scheduler.http.response.size.bytes.percentile.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.scheduler.http.response.size.bytes.percentile.*" - } - }, - { - "kubernetes.scheduler.scheduling.e2e.duration.us.bucket.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "kubernetes.scheduler.scheduling.e2e.duration.us.bucket.*" - } - }, - { - "kubernetes.scheduler.scheduling.duration.seconds.percentile.*": { - "mapping": { - "type": "double" - }, - "match_mapping_type": "double", - "path_match": "kubernetes.scheduler.scheduling.duration.seconds.percentile.*" - } - }, - { - "munin.metrics.*": { - "mapping": { - "type": "double" - }, - "path_match": "munin.metrics.*" - } - }, - { - "prometheus.labels.*": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "prometheus.labels.*" - } - }, - { - "prometheus.metrics.*": { - "mapping": { - "type": "double" - }, - "path_match": "prometheus.metrics.*" - } - }, - { - "prometheus.query.*": { - "mapping": { - "type": "double" - }, - "path_match": "prometheus.query.*" - } - }, - { - "system.process.env": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "system.process.env.*" - } - }, - { - "system.process.cgroup.cpuacct.percpu": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "system.process.cgroup.cpuacct.percpu.*" - } - }, - { - "system.raid.disks.states.*": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "system.raid.disks.states.*" - } - }, - { - "traefik.health.response.status_codes.*": { - "mapping": { - "type": "long" - }, - "match_mapping_type": "long", - "path_match": "traefik.health.response.status_codes.*" - } - }, - { - "vsphere.virtualmachine.custom_fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "vsphere.virtualmachine.custom_fields.*" - } - }, - { - "windows.perfmon.metrics.*.*": { - "mapping": { - "type": "float" - }, - "path_match": "windows.perfmon.metrics.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "aerospike": { - "properties": { - "namespace": { - "properties": { - "client": { - "properties": { - "delete": { - "properties": { - "error": { - "type": "long" - }, - "not_found": { - "type": "long" - }, - "success": { - "type": "long" - }, - "timeout": { - "type": "long" - } - } - }, - "read": { - "properties": { - "error": { - "type": "long" - }, - "not_found": { - "type": "long" - }, - "success": { - "type": "long" - }, - "timeout": { - "type": "long" - } - } - }, - "write": { - "properties": { - "error": { - "type": "long" - }, - "success": { - "type": "long" - }, - "timeout": { - "type": "long" - } - } - } - } - }, - "device": { - "properties": { - "available": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "free": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "hwm_breached": { - "type": "boolean" - }, - "memory": { - "properties": { - "free": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "used": { - "properties": { - "data": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "index": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "sindex": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "objects": { - "properties": { - "master": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "stop_writes": { - "type": "boolean" - } - } - } - } - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "apache": { - "properties": { - "status": { - "properties": { - "bytes_per_request": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "bytes_per_sec": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "connections": { - "properties": { - "async": { - "properties": { - "closing": { - "type": "long" - }, - "keep_alive": { - "type": "long" - }, - "writing": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "children_system": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "children_user": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "load": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "system": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "user": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "load": { - "properties": { - "1": { - "scaling_factor": 100, - "type": "scaled_float" - }, - "15": { - "scaling_factor": 100, - "type": "scaled_float" - }, - "5": { - "scaling_factor": 100, - "type": "scaled_float" - } - } - }, - "requests_per_sec": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "scoreboard": { - "properties": { - "closing_connection": { - "type": "long" - }, - "dns_lookup": { - "type": "long" - }, - "gracefully_finishing": { - "type": "long" - }, - "idle_cleanup": { - "type": "long" - }, - "keepalive": { - "type": "long" - }, - "logging": { - "type": "long" - }, - "open_slot": { - "type": "long" - }, - "reading_request": { - "type": "long" - }, - "sending_reply": { - "type": "long" - }, - "starting_up": { - "type": "long" - }, - "total": { - "type": "long" - }, - "waiting_for_connection": { - "type": "long" - } - } - }, - "total_accesses": { - "type": "long" - }, - "total_kbytes": { - "type": "long" - }, - "uptime": { - "properties": { - "server_uptime": { - "type": "long" - }, - "uptime": { - "type": "long" - } - } - }, - "workers": { - "properties": { - "busy": { - "type": "long" - }, - "idle": { - "type": "long" - } - } - } - } - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "beat": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "properties": { - "beat": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cluster": { - "properties": { - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "containerized": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "input": { - "properties": { - "count": { - "type": "long" - }, - "names": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "management": { - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "module": { - "properties": { - "count": { - "type": "long" - }, - "names": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "output": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "apm-server": { - "properties": { - "acm": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - } - } - }, - "response": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "properties": { - "closed": { - "type": "long" - }, - "count": { - "type": "long" - }, - "decode": { - "type": "long" - }, - "forbidden": { - "type": "long" - }, - "internal": { - "type": "long" - }, - "invalidquery": { - "type": "long" - }, - "method": { - "type": "long" - }, - "notfound": { - "type": "long" - }, - "queue": { - "type": "long" - }, - "ratelimit": { - "type": "long" - }, - "toolarge": { - "type": "long" - }, - "unauthorized": { - "type": "long" - }, - "unavailable": { - "type": "long" - }, - "validate": { - "type": "long" - } - } - }, - "request": { - "properties": { - "count": { - "type": "long" - } - } - }, - "unset": { - "type": "long" - }, - "valid": { - "properties": { - "accepted": { - "type": "long" - }, - "count": { - "type": "long" - }, - "notmodified": { - "type": "long" - }, - "ok": { - "type": "long" - } - } - } - } - } - } - }, - "decoder": { - "properties": { - "deflate": { - "properties": { - "content-length": { - "type": "long" - }, - "count": { - "type": "long" - } - } - }, - "gzip": { - "properties": { - "content-length": { - "type": "long" - }, - "count": { - "type": "long" - } - } - }, - "missing-content-length": { - "properties": { - "count": { - "type": "long" - } - } - }, - "reader": { - "properties": { - "count": { - "type": "long" - }, - "size": { - "type": "long" - } - } - }, - "uncompressed": { - "properties": { - "content-length": { - "type": "long" - }, - "count": { - "type": "long" - } - } - } - } - }, - "processor": { - "properties": { - "error": { - "properties": { - "decoding": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - }, - "frames": { - "type": "long" - }, - "spans": { - "type": "long" - }, - "stacktraces": { - "type": "long" - }, - "transformations": { - "type": "long" - }, - "validation": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - } - } - }, - "metric": { - "properties": { - "decoding": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - }, - "transformations": { - "type": "long" - }, - "validation": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - } - } - }, - "sourcemap": { - "properties": { - "counter": { - "type": "long" - }, - "decoding": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - }, - "validation": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - } - } - }, - "span": { - "properties": { - "transformations": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "decoding": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - }, - "frames": { - "type": "long" - }, - "spans": { - "type": "long" - }, - "stacktraces": { - "type": "long" - }, - "transactions": { - "type": "long" - }, - "transformations": { - "type": "long" - }, - "validation": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - } - } - } - } - }, - "server": { - "properties": { - "concurrent": { - "properties": { - "wait": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "request": { - "properties": { - "count": { - "type": "long" - } - } - }, - "response": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "properties": { - "closed": { - "type": "long" - }, - "concurrency": { - "type": "long" - }, - "count": { - "type": "long" - }, - "decode": { - "type": "long" - }, - "forbidden": { - "type": "long" - }, - "internal": { - "type": "long" - }, - "method": { - "type": "long" - }, - "queue": { - "type": "long" - }, - "ratelimit": { - "type": "long" - }, - "toolarge": { - "type": "long" - }, - "unauthorized": { - "type": "long" - }, - "validate": { - "type": "long" - } - } - }, - "valid": { - "properties": { - "accepted": { - "type": "long" - }, - "count": { - "type": "long" - }, - "ok": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "beat": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs": { - "properties": { - "period": { - "properties": { - "us": { - "type": "long" - } - } - }, - "quota": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "stats": { - "properties": { - "periods": { - "type": "long" - }, - "throttled": { - "properties": { - "ns": { - "type": "long" - }, - "periods": { - "type": "long" - } - } - } - } - } - } - }, - "cpuacct": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "mem": { - "properties": { - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "cpu": { - "properties": { - "system": { - "properties": { - "ticks": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "ticks": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "value": { - "type": "long" - } - } - }, - "user": { - "properties": { - "ticks": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "handles": { - "properties": { - "limit": { - "properties": { - "hard": { - "type": "long" - }, - "soft": { - "type": "long" - } - } - }, - "open": { - "type": "long" - } - } - }, - "info": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "libbeat": { - "properties": { - "config": { - "properties": { - "reloads": { - "type": "short" - }, - "running": { - "type": "short" - }, - "starts": { - "type": "short" - }, - "stops": { - "type": "short" - } - } - }, - "output": { - "properties": { - "events": { - "properties": { - "acked": { - "type": "long" - }, - "active": { - "type": "long" - }, - "batches": { - "type": "long" - }, - "dropped": { - "type": "long" - }, - "duplicates": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "toomany": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "read": { - "properties": { - "bytes": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "write": { - "properties": { - "bytes": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - } - } - }, - "pipeline": { - "properties": { - "clients": { - "type": "long" - }, - "events": { - "properties": { - "active": { - "type": "long" - }, - "dropped": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "published": { - "type": "long" - }, - "retry": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "acked": { - "type": "long" - } - } - } - } - } - } - }, - "memstats": { - "properties": { - "gc_next": { - "type": "long" - }, - "memory": { - "properties": { - "alloc": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "rss": { - "type": "long" - } - } - }, - "runtime": { - "properties": { - "goroutines": { - "type": "long" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "cores": { - "type": "long" - } - } - }, - "load": { - "properties": { - "1": { - "type": "double" - }, - "15": { - "type": "double" - }, - "5": { - "type": "double" - }, - "norm": { - "properties": { - "1": { - "type": "double" - }, - "15": { - "type": "double" - }, - "5": { - "type": "double" - } - } - } - } - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "beats_state": { - "properties": { - "beat": { - "properties": { - "host": { - "path": "beat.state.beat.host", - "type": "alias" - }, - "name": { - "path": "beat.state.beat.name", - "type": "alias" - }, - "type": { - "path": "beat.state.beat.type", - "type": "alias" - }, - "uuid": { - "path": "beat.state.beat.uuid", - "type": "alias" - }, - "version": { - "path": "beat.state.beat.version", - "type": "alias" - } - } - }, - "state": { - "properties": { - "beat": { - "properties": { - "name": { - "path": "beat.state.beat.name", - "type": "alias" - } - } - }, - "host": { - "properties": { - "architecture": { - "path": "host.architecture", - "type": "alias" - }, - "hostname": { - "path": "host.hostname", - "type": "alias" - }, - "name": { - "path": "host.name", - "type": "alias" - }, - "os": { - "properties": { - "platform": { - "path": "beat.state.host.os.platform", - "type": "alias" - }, - "version": { - "path": "beat.state.host.os.version", - "type": "alias" - } - } - } - } - }, - "input": { - "properties": { - "count": { - "path": "beat.state.input.count", - "type": "alias" - }, - "names": { - "path": "beat.state.input.names", - "type": "alias" - } - } - }, - "module": { - "properties": { - "count": { - "path": "beat.state.module.count", - "type": "alias" - }, - "names": { - "path": "beat.state.module.names", - "type": "alias" - } - } - }, - "output": { - "properties": { - "name": { - "path": "beat.state.output.name", - "type": "alias" - } - } - }, - "service": { - "properties": { - "id": { - "path": "beat.state.service.id", - "type": "alias" - }, - "name": { - "path": "beat.state.service.name", - "type": "alias" - }, - "version": { - "path": "beat.state.service.version", - "type": "alias" - } - } - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "beats_stats": { - "properties": { - "beat": { - "properties": { - "host": { - "path": "beat.stats.beat.host", - "type": "alias" - }, - "name": { - "path": "beat.stats.beat.name", - "type": "alias" - }, - "type": { - "path": "beat.stats.beat.type", - "type": "alias" - }, - "uuid": { - "path": "beat.stats.beat.uuid", - "type": "alias" - }, - "version": { - "path": "beat.stats.beat.version", - "type": "alias" - } - } - }, - "metrics": { - "properties": { - "apm-server": { - "properties": { - "acm": { - "properties": { - "request": { - "properties": { - "count": { - "path": "beat.stats.apm-server.acm.request.count", - "type": "alias" - } - } - }, - "response": { - "properties": { - "count": { - "path": "beat.stats.apm-server.acm.response.count", - "type": "alias" - }, - "errors": { - "properties": { - "closed": { - "path": "beat.stats.apm-server.acm.response.errors.closed", - "type": "alias" - }, - "count": { - "path": "beat.stats.apm-server.acm.response.errors.count", - "type": "alias" - }, - "decode": { - "path": "beat.stats.apm-server.acm.response.errors.decode", - "type": "alias" - }, - "forbidden": { - "path": "beat.stats.apm-server.acm.response.errors.forbidden", - "type": "alias" - }, - "internal": { - "path": "beat.stats.apm-server.acm.response.errors.internal", - "type": "alias" - }, - "invalidquery": { - "path": "beat.stats.apm-server.acm.response.errors.invalidquery", - "type": "alias" - }, - "method": { - "path": "beat.stats.apm-server.acm.response.errors.method", - "type": "alias" - }, - "notfound": { - "path": "beat.stats.apm-server.acm.response.errors.notfound", - "type": "alias" - }, - "queue": { - "path": "beat.stats.apm-server.acm.response.errors.queue", - "type": "alias" - }, - "ratelimit": { - "path": "beat.stats.apm-server.acm.response.errors.ratelimit", - "type": "alias" - }, - "toolarge": { - "path": "beat.stats.apm-server.acm.response.errors.toolarge", - "type": "alias" - }, - "unauthorized": { - "path": "beat.stats.apm-server.acm.response.errors.unauthorized", - "type": "alias" - }, - "unavailable": { - "path": "beat.stats.apm-server.acm.response.errors.unavailable", - "type": "alias" - }, - "validate": { - "path": "beat.stats.apm-server.acm.response.errors.validate", - "type": "alias" - } - } - }, - "request": { - "properties": { - "count": { - "path": "beat.stats.apm-server.acm.response.request.count", - "type": "alias" - } - } - }, - "unset": { - "path": "beat.stats.apm-server.acm.response.unset", - "type": "alias" - }, - "valid": { - "properties": { - "accepted": { - "path": "beat.stats.apm-server.acm.response.valid.accepted", - "type": "alias" - }, - "count": { - "path": "beat.stats.apm-server.acm.response.valid.count", - "type": "alias" - }, - "notmodified": { - "path": "beat.stats.apm-server.acm.response.valid.notmodified", - "type": "alias" - }, - "ok": { - "path": "beat.stats.apm-server.acm.response.valid.ok", - "type": "alias" - } - } - } - } - } - } - }, - "decoder": { - "properties": { - "deflate": { - "properties": { - "content-length": { - "path": "beat.stats.apm-server.decoder.deflate.content-length", - "type": "alias" - }, - "count": { - "path": "beat.stats.apm-server.decoder.deflate.count", - "type": "alias" - } - } - }, - "gzip": { - "properties": { - "content-length": { - "path": "beat.stats.apm-server.decoder.gzip.content-length", - "type": "alias" - }, - "count": { - "path": "beat.stats.apm-server.decoder.gzip.count", - "type": "alias" - } - } - }, - "missing-content-length": { - "properties": { - "count": { - "path": "beat.stats.apm-server.decoder.missing-content-length.count", - "type": "alias" - } - } - }, - "reader": { - "properties": { - "count": { - "path": "beat.stats.apm-server.decoder.reader.count", - "type": "alias" - }, - "size": { - "path": "beat.stats.apm-server.decoder.reader.size", - "type": "alias" - } - } - }, - "uncompressed": { - "properties": { - "content-length": { - "path": "beat.stats.apm-server.decoder.uncompressed.content-length", - "type": "alias" - }, - "count": { - "path": "beat.stats.apm-server.decoder.uncompressed.count", - "type": "alias" - } - } - } - } - }, - "processor": { - "properties": { - "error": { - "properties": { - "decoding": { - "properties": { - "count": { - "path": "beat.stats.apm-server.processor.error.decoding.count", - "type": "alias" - }, - "errors": { - "path": "beat.stats.apm-server.processor.error.decoding.errors", - "type": "alias" - } - } - }, - "frames": { - "path": "beat.stats.apm-server.processor.error.frames", - "type": "alias" - }, - "spans": { - "path": "beat.stats.apm-server.processor.error.spans", - "type": "alias" - }, - "stacktraces": { - "path": "beat.stats.apm-server.processor.error.stacktraces", - "type": "alias" - }, - "transformations": { - "path": "beat.stats.apm-server.processor.error.transformations", - "type": "alias" - }, - "validation": { - "properties": { - "count": { - "path": "beat.stats.apm-server.processor.error.validation.count", - "type": "alias" - }, - "errors": { - "path": "beat.stats.apm-server.processor.error.validation.errors", - "type": "alias" - } - } - } - } - }, - "metric": { - "properties": { - "decoding": { - "properties": { - "count": { - "path": "beat.stats.apm-server.processor.metric.decoding.count", - "type": "alias" - }, - "errors": { - "path": "beat.stats.apm-server.processor.metric.decoding.errors", - "type": "alias" - } - } - }, - "transformations": { - "path": "beat.stats.apm-server.processor.metric.transformations", - "type": "alias" - }, - "validation": { - "properties": { - "count": { - "path": "beat.stats.apm-server.processor.metric.validation.count", - "type": "alias" - }, - "errors": { - "path": "beat.stats.apm-server.processor.metric.validation.errors", - "type": "alias" - } - } - } - } - }, - "sourcemap": { - "properties": { - "counter": { - "path": "beat.stats.apm-server.processor.sourcemap.counter", - "type": "alias" - }, - "decoding": { - "properties": { - "count": { - "path": "beat.stats.apm-server.processor.sourcemap.decoding.count", - "type": "alias" - }, - "errors": { - "path": "beat.stats.apm-server.processor.sourcemap.decoding.errors", - "type": "alias" - } - } - }, - "validation": { - "properties": { - "count": { - "path": "beat.stats.apm-server.processor.sourcemap.validation.count", - "type": "alias" - }, - "errors": { - "path": "beat.stats.apm-server.processor.sourcemap.validation.errors", - "type": "alias" - } - } - } - } - }, - "span": { - "properties": { - "transformations": { - "path": "beat.stats.apm-server.processor.span.transformations", - "type": "alias" - } - } - }, - "transaction": { - "properties": { - "decoding": { - "properties": { - "count": { - "path": "beat.stats.apm-server.processor.transaction.decoding.count", - "type": "alias" - }, - "errors": { - "path": "beat.stats.apm-server.processor.transaction.decoding.errors", - "type": "alias" - } - } - }, - "frames": { - "path": "beat.stats.apm-server.processor.transaction.frames", - "type": "alias" - }, - "spans": { - "path": "beat.stats.apm-server.processor.transaction.spans", - "type": "alias" - }, - "stacktraces": { - "path": "beat.stats.apm-server.processor.transaction.stacktraces", - "type": "alias" - }, - "transactions": { - "path": "beat.stats.apm-server.processor.transaction.transactions", - "type": "alias" - }, - "transformations": { - "path": "beat.stats.apm-server.processor.transaction.transformations", - "type": "alias" - }, - "validation": { - "properties": { - "count": { - "path": "beat.stats.apm-server.processor.transaction.validation.count", - "type": "alias" - }, - "errors": { - "path": "beat.stats.apm-server.processor.transaction.validation.errors", - "type": "alias" - } - } - } - } - } - } - }, - "server": { - "properties": { - "concurrent": { - "properties": { - "wait": { - "properties": { - "ms": { - "path": "beat.stats.apm-server.server.concurrent.wait.ms", - "type": "alias" - } - } - } - } - }, - "request": { - "properties": { - "count": { - "path": "beat.stats.apm-server.server.request.count", - "type": "alias" - } - } - }, - "response": { - "properties": { - "count": { - "path": "beat.stats.apm-server.server.response.count", - "type": "alias" - }, - "errors": { - "properties": { - "closed": { - "path": "beat.stats.apm-server.server.response.errors.closed", - "type": "alias" - }, - "concurrency": { - "path": "beat.stats.apm-server.server.response.errors.concurrency", - "type": "alias" - }, - "count": { - "path": "beat.stats.apm-server.server.response.errors.count", - "type": "alias" - }, - "decode": { - "path": "beat.stats.apm-server.server.response.errors.decode", - "type": "alias" - }, - "forbidden": { - "path": "beat.stats.apm-server.server.response.errors.forbidden", - "type": "alias" - }, - "internal": { - "path": "beat.stats.apm-server.server.response.errors.internal", - "type": "alias" - }, - "method": { - "path": "beat.stats.apm-server.server.response.errors.method", - "type": "alias" - }, - "queue": { - "path": "beat.stats.apm-server.server.response.errors.queue", - "type": "alias" - }, - "ratelimit": { - "path": "beat.stats.apm-server.server.response.errors.ratelimit", - "type": "alias" - }, - "toolarge": { - "path": "beat.stats.apm-server.server.response.errors.toolarge", - "type": "alias" - }, - "unauthorized": { - "path": "beat.stats.apm-server.server.response.errors.unauthorized", - "type": "alias" - }, - "validate": { - "path": "beat.stats.apm-server.server.response.errors.validate", - "type": "alias" - } - } - }, - "valid": { - "properties": { - "accepted": { - "path": "beat.stats.apm-server.server.response.valid.accepted", - "type": "alias" - }, - "count": { - "path": "beat.stats.apm-server.server.response.valid.count", - "type": "alias" - }, - "ok": { - "path": "beat.stats.apm-server.server.response.valid.ok", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "beat": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs": { - "properties": { - "period": { - "properties": { - "us": { - "path": "beat.stats.cgroup.cpu.cfs.period.us", - "type": "alias" - } - } - }, - "quota": { - "properties": { - "us": { - "path": "beat.stats.cgroup.cpu.cfs.quota.us", - "type": "alias" - } - } - } - } - }, - "id": { - "path": "beat.stats.cgroup.cpu.id", - "type": "alias" - }, - "stats": { - "properties": { - "periods": { - "path": "beat.stats.cgroup.cpu.stats.periods", - "type": "alias" - }, - "throttled": { - "properties": { - "ns": { - "path": "beat.stats.cgroup.cpu.stats.throttled.ns", - "type": "alias" - }, - "periods": { - "path": "beat.stats.cgroup.cpu.stats.throttled.periods", - "type": "alias" - } - } - } - } - } - } - }, - "cpuacct": { - "properties": { - "id": { - "path": "beat.stats.cgroup.cpuacct.id", - "type": "alias" - }, - "total": { - "properties": { - "ns": { - "path": "beat.stats.cgroup.cpuacct.total.ns", - "type": "alias" - } - } - } - } - }, - "mem": { - "properties": { - "limit": { - "properties": { - "bytes": { - "path": "beat.stats.cgroup.memory.mem.limit.bytes", - "type": "alias" - } - } - }, - "usage": { - "properties": { - "bytes": { - "path": "beat.stats.cgroup.memory.mem.usage.bytes", - "type": "alias" - } - } - } - } - }, - "memory": { - "properties": { - "id": { - "path": "beat.stats.cgroup.memory.id", - "type": "alias" - } - } - } - } - }, - "cpu": { - "properties": { - "system": { - "properties": { - "ticks": { - "path": "beat.stats.cpu.system.ticks", - "type": "alias" - }, - "time": { - "properties": { - "ms": { - "path": "beat.stats.cpu.system.time.ms", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "ticks": { - "path": "beat.stats.cpu.total.ticks", - "type": "alias" - }, - "time": { - "properties": { - "ms": { - "path": "beat.stats.cpu.total.time.ms", - "type": "alias" - } - } - }, - "value": { - "path": "beat.stats.cpu.total.value", - "type": "alias" - } - } - }, - "user": { - "properties": { - "ticks": { - "path": "beat.stats.cpu.user.ticks", - "type": "alias" - }, - "time": { - "properties": { - "ms": { - "path": "beat.stats.cpu.user.time.ms", - "type": "alias" - } - } - } - } - } - } - }, - "handles": { - "properties": { - "limit": { - "properties": { - "hard": { - "path": "beat.stats.handles.limit.hard", - "type": "alias" - }, - "soft": { - "path": "beat.stats.handles.limit.soft", - "type": "alias" - } - } - }, - "open": { - "path": "beat.stats.handles.open", - "type": "alias" - } - } - }, - "info": { - "properties": { - "ephemeral_id": { - "path": "beat.stats.info.ephemeral_id", - "type": "alias" - }, - "uptime": { - "properties": { - "ms": { - "path": "beat.stats.info.uptime.ms", - "type": "alias" - } - } - } - } - }, - "memstats": { - "properties": { - "gc_next": { - "path": "beat.stats.memstats.gc_next", - "type": "alias" - }, - "memory_alloc": { - "path": "beat.stats.memstats.memory.alloc", - "type": "alias" - }, - "memory_total": { - "path": "beat.stats.memstats.memory.total", - "type": "alias" - }, - "rss": { - "path": "beat.stats.memstats.rss", - "type": "alias" - } - } - } - } - }, - "libbeat": { - "properties": { - "config": { - "properties": { - "module": { - "properties": { - "running": { - "path": "beat.stats.libbeat.config.running", - "type": "alias" - }, - "starts": { - "path": "beat.stats.libbeat.config.starts", - "type": "alias" - }, - "stops": { - "path": "beat.stats.libbeat.config.stops", - "type": "alias" - } - } - }, - "reloads": { - "path": "beat.stats.libbeat.config.reloads", - "type": "alias" - } - } - }, - "output": { - "properties": { - "events": { - "properties": { - "acked": { - "path": "beat.stats.libbeat.output.events.acked", - "type": "alias" - }, - "active": { - "path": "beat.stats.libbeat.output.events.active", - "type": "alias" - }, - "batches": { - "path": "beat.stats.libbeat.output.events.batches", - "type": "alias" - }, - "dropped": { - "path": "beat.stats.libbeat.output.events.dropped", - "type": "alias" - }, - "duplicated": { - "path": "beat.stats.libbeat.output.events.duplicates", - "type": "alias" - }, - "failed": { - "path": "beat.stats.libbeat.output.events.failed", - "type": "alias" - }, - "toomany": { - "path": "beat.stats.libbeat.output.events.toomany", - "type": "alias" - }, - "total": { - "path": "beat.stats.libbeat.output.events.total", - "type": "alias" - } - } - }, - "read": { - "properties": { - "bytes": { - "path": "beat.stats.libbeat.output.read.bytes", - "type": "alias" - }, - "errors": { - "path": "beat.stats.libbeat.output.read.errors", - "type": "alias" - } - } - }, - "type": { - "path": "beat.stats.libbeat.output.type", - "type": "alias" - }, - "write": { - "properties": { - "bytes": { - "path": "beat.stats.libbeat.output.write.bytes", - "type": "alias" - }, - "errors": { - "path": "beat.stats.libbeat.output.write.errors", - "type": "alias" - } - } - } - } - }, - "pipeline": { - "properties": { - "clients": { - "path": "beat.stats.libbeat.pipeline.clients", - "type": "alias" - }, - "events": { - "properties": { - "active": { - "path": "beat.stats.libbeat.pipeline.events.active", - "type": "alias" - }, - "dropped": { - "path": "beat.stats.libbeat.pipeline.events.dropped", - "type": "alias" - }, - "failed": { - "path": "beat.stats.libbeat.pipeline.events.failed", - "type": "alias" - }, - "filtered": { - "path": "beat.stats.libbeat.pipeline.events.filtered", - "type": "alias" - }, - "published": { - "path": "beat.stats.libbeat.pipeline.events.published", - "type": "alias" - }, - "retry": { - "path": "beat.stats.libbeat.pipeline.events.retry", - "type": "alias" - }, - "total": { - "path": "beat.stats.libbeat.pipeline.events.total", - "type": "alias" - } - } - }, - "queue": { - "properties": { - "acked": { - "path": "beat.stats.libbeat.pipeline.queue.acked", - "type": "alias" - } - } - } - } - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "cores": { - "path": "beat.stats.system.cpu.cores", - "type": "alias" - } - } - }, - "load": { - "properties": { - "1": { - "path": "beat.stats.system.load.1", - "type": "alias" - }, - "15": { - "path": "beat.stats.system.load.15", - "type": "alias" - }, - "5": { - "path": "beat.stats.system.load.5", - "type": "alias" - }, - "norm": { - "properties": { - "1": { - "path": "beat.stats.system.load.norm.1", - "type": "alias" - }, - "15": { - "path": "beat.stats.system.load.norm.15", - "type": "alias" - }, - "5": { - "path": "beat.stats.system.load.norm.5", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "ccr_auto_follow_stats": { - "properties": { - "follower": { - "properties": { - "failed_read_requests": { - "path": "elasticsearch.ccr.requests.failed.read.count", - "type": "alias" - } - } - }, - "number_of_failed_follow_indices": { - "path": "elasticsearch.ccr.auto_follow.failed.follow_indices.count", - "type": "alias" - }, - "number_of_failed_remote_cluster_state_requests": { - "path": "elasticsearch.ccr.auto_follow.failed.remote_cluster_state_requests.count", - "type": "alias" - }, - "number_of_successful_follow_indices": { - "path": "elasticsearch.ccr.auto_follow.success.follow_indices.count", - "type": "alias" - } - } - }, - "ccr_stats": { - "properties": { - "bytes_read": { - "path": "elasticsearch.ccr.bytes_read", - "type": "alias" - }, - "failed_read_requests": { - "path": "elasticsearch.ccr.requests.failed.read.count", - "type": "alias" - }, - "failed_write_requests": { - "path": "elasticsearch.ccr.requests.failed.write.count", - "type": "alias" - }, - "follower_aliases_version": { - "path": "elasticsearch.ccr.follower.aliases_version", - "type": "alias" - }, - "follower_global_checkpoint": { - "path": "elasticsearch.ccr.follower.global_checkpoint", - "type": "alias" - }, - "follower_index": { - "path": "elasticsearch.ccr.follower.index", - "type": "alias" - }, - "follower_mapping_version": { - "path": "elasticsearch.ccr.follower.mapping_version", - "type": "alias" - }, - "follower_max_seq_no": { - "path": "elasticsearch.ccr.follower.max_seq_no", - "type": "alias" - }, - "follower_settings_version": { - "path": "elasticsearch.ccr.follower.settings_version", - "type": "alias" - }, - "last_requested_seq_no": { - "path": "elasticsearch.ccr.last_requested_seq_no", - "type": "alias" - }, - "leader_global_checkpoint": { - "path": "elasticsearch.ccr.leader.global_checkpoint", - "type": "alias" - }, - "leader_index": { - "path": "elasticsearch.ccr.leader.index", - "type": "alias" - }, - "leader_max_seq_no": { - "path": "elasticsearch.ccr.leader.max_seq_no", - "type": "alias" - }, - "operations_read": { - "path": "elasticsearch.ccr.follower.operations.read.count", - "type": "alias" - }, - "operations_written": { - "path": "elasticsearch.ccr.follower.operations_written", - "type": "alias" - }, - "outstanding_read_requests": { - "path": "elasticsearch.ccr.requests.outstanding.read.count", - "type": "alias" - }, - "outstanding_write_requests": { - "path": "elasticsearch.ccr.requests.outstanding.write.count", - "type": "alias" - }, - "remote_cluster": { - "path": "elasticsearch.ccr.remote_cluster", - "type": "alias" - }, - "shard_id": { - "path": "elasticsearch.ccr.follower.shard.number", - "type": "alias" - }, - "successful_read_requests": { - "path": "elasticsearch.ccr.requests.successful.read.count", - "type": "alias" - }, - "successful_write_requests": { - "path": "elasticsearch.ccr.requests.successful.write.count", - "type": "alias" - }, - "total_read_remote_exec_time_millis": { - "path": "elasticsearch.ccr.total_time.read.remote_exec.ms", - "type": "alias" - }, - "total_read_time_millis": { - "path": "elasticsearch.ccr.total_time.read.ms", - "type": "alias" - }, - "total_write_time_millis": { - "path": "elasticsearch.ccr.total_time.write.ms", - "type": "alias" - }, - "write_buffer_operation_count": { - "path": "elasticsearch.ccr.write_buffer.operation.count", - "type": "alias" - }, - "write_buffer_size_in_bytes": { - "path": "elasticsearch.ccr.write_buffer.size.bytes", - "type": "alias" - } - } - }, - "ceph": { - "properties": { - "cluster_disk": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "cluster_health": { - "properties": { - "overall_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "timechecks": { - "properties": { - "epoch": { - "type": "long" - }, - "round": { - "properties": { - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "type": "long" - } - } - } - } - } - } - }, - "cluster_status": { - "properties": { - "degraded": { - "properties": { - "objects": { - "type": "long" - }, - "ratio": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "total": { - "type": "long" - } - } - }, - "misplace": { - "properties": { - "objects": { - "type": "long" - }, - "ratio": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "total": { - "type": "long" - } - } - }, - "osd": { - "properties": { - "epoch": { - "type": "long" - }, - "full": { - "type": "boolean" - }, - "nearfull": { - "type": "boolean" - }, - "num_in_osds": { - "type": "long" - }, - "num_osds": { - "type": "long" - }, - "num_remapped_pgs": { - "type": "long" - }, - "num_up_osds": { - "type": "long" - } - } - }, - "pg": { - "properties": { - "avail_bytes": { - "type": "long" - }, - "data_bytes": { - "type": "long" - }, - "total_bytes": { - "type": "long" - }, - "used_bytes": { - "type": "long" - } - } - }, - "pg_state": { - "properties": { - "count": { - "type": "long" - }, - "state_name": { - "type": "long" - }, - "version": { - "type": "long" - } - } - }, - "traffic": { - "properties": { - "read_bytes": { - "type": "long" - }, - "read_op_per_sec": { - "type": "long" - }, - "write_bytes": { - "type": "long" - }, - "write_op_per_sec": { - "type": "long" - } - } - }, - "version": { - "type": "long" - } - } - }, - "mgr_osd_perf": { - "properties": { - "id": { - "type": "long" - }, - "stats": { - "properties": { - "apply_latency_ms": { - "type": "long" - }, - "apply_latency_ns": { - "type": "long" - }, - "commit_latency_ms": { - "type": "long" - }, - "commit_latency_ns": { - "type": "long" - } - } - } - } - }, - "mgr_osd_pool_stats": { - "properties": { - "client_io_rate": { - "type": "object" - }, - "pool_id": { - "type": "long" - }, - "pool_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "monitor_health": { - "properties": { - "available": { - "properties": { - "kb": { - "type": "long" - }, - "pct": { - "type": "long" - } - } - }, - "health": { - "ignore_above": 1024, - "type": "keyword" - }, - "last_updated": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "store_stats": { - "properties": { - "last_updated": { - "type": "long" - }, - "log": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "misc": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "sst": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "kb": { - "type": "long" - } - } - }, - "used": { - "properties": { - "kb": { - "type": "long" - } - } - } - } - }, - "osd_df": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "device_class": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "pg_num": { - "type": "long" - }, - "total": { - "properties": { - "byte": { - "type": "long" - } - } - }, - "used": { - "properties": { - "byte": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "osd_tree": { - "properties": { - "children": { - "ignore_above": 1024, - "type": "keyword" - }, - "crush_weight": { - "type": "float" - }, - "depth": { - "type": "long" - }, - "device_class": { - "ignore_above": 1024, - "type": "keyword" - }, - "exists": { - "type": "boolean" - }, - "father": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primary_affinity": { - "type": "float" - }, - "reweight": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "type_id": { - "type": "long" - } - } - }, - "pool_disk": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "stats": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "objects": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "kb": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cluster_state": { - "properties": { - "master_node": { - "path": "elasticsearch.cluster.stats.state.master_node", - "type": "alias" - }, - "nodes_hash": { - "path": "elasticsearch.cluster.stats.state.nodes_hash", - "type": "alias" - }, - "state_uuid": { - "path": "elasticsearch.cluster.stats.state.state_uuid", - "type": "alias" - }, - "status": { - "path": "elasticsearch.cluster.stats.status", - "type": "alias" - }, - "version": { - "path": "elasticsearch.cluster.stats.state.version", - "type": "alias" - } - } - }, - "cluster_stats": { - "properties": { - "indices": { - "properties": { - "count": { - "path": "elasticsearch.cluster.stats.indices.total", - "type": "alias" - }, - "shards": { - "properties": { - "total": { - "path": "elasticsearch.cluster.stats.indices.shards.count", - "type": "alias" - } - } - } - } - }, - "nodes": { - "properties": { - "count": { - "properties": { - "total": { - "path": "elasticsearch.cluster.stats.nodes.count", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "max_uptime_in_millis": { - "path": "elasticsearch.cluster.stats.nodes.jvm.max_uptime.ms", - "type": "alias" - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.max.bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.used.bytes", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "cluster_uuid": { - "path": "elasticsearch.cluster.id", - "type": "alias" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "consul": { - "properties": { - "agent": { - "properties": { - "autopilot": { - "properties": { - "healthy": { - "type": "boolean" - } - } - }, - "runtime": { - "properties": { - "alloc": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "garbage_collector": { - "properties": { - "pause": { - "properties": { - "current": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "total": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "runs": { - "type": "long" - } - } - }, - "goroutines": { - "type": "long" - }, - "heap_objects": { - "type": "long" - }, - "malloc_count": { - "type": "long" - }, - "sys": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "container": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "couchbase": { - "properties": { - "bucket": { - "properties": { - "data": { - "properties": { - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "disk": { - "properties": { - "fetches": { - "type": "double" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "item_count": { - "type": "long" - }, - "memory": { - "properties": { - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ops_per_sec": { - "type": "double" - }, - "quota": { - "properties": { - "ram": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "use": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cluster": { - "properties": { - "hdd": { - "properties": { - "free": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "quota": { - "properties": { - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "by_data": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "value": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "max_bucket_count": { - "type": "long" - }, - "quota": { - "properties": { - "index_memory": { - "properties": { - "mb": { - "type": "double" - } - } - }, - "memory": { - "properties": { - "mb": { - "type": "double" - } - } - } - } - }, - "ram": { - "properties": { - "quota": { - "properties": { - "total": { - "properties": { - "per_node": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "value": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "used": { - "properties": { - "per_node": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "value": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "by_data": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "value": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "node": { - "properties": { - "cmd_get": { - "type": "double" - }, - "couch": { - "properties": { - "docs": { - "properties": { - "data_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "disk_size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "spatial": { - "properties": { - "data_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "disk_size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "views": { - "properties": { - "data_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "disk_size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "cpu_utilization_rate": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "current_items": { - "properties": { - "total": { - "type": "long" - }, - "value": { - "type": "long" - } - } - }, - "ep_bg_fetched": { - "type": "long" - }, - "get_hits": { - "type": "double" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "mcd_memory": { - "properties": { - "allocated": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "reserved": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "free": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "ops": { - "type": "double" - }, - "swap": { - "properties": { - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "uptime": { - "properties": { - "sec": { - "type": "long" - } - } - }, - "vb_replica_curr_items": { - "type": "long" - } - } - } - } - }, - "couchdb": { - "properties": { - "server": { - "properties": { - "couchdb": { - "properties": { - "auth_cache_hits": { - "type": "long" - }, - "auth_cache_misses": { - "type": "long" - }, - "database_reads": { - "type": "long" - }, - "database_writes": { - "type": "long" - }, - "open_databases": { - "type": "long" - }, - "open_os_files": { - "type": "long" - }, - "request_time": { - "type": "long" - } - } - }, - "httpd": { - "properties": { - "bulk_requests": { - "type": "long" - }, - "clients_requesting_changes": { - "type": "long" - }, - "requests": { - "type": "long" - }, - "temporary_view_reads": { - "type": "long" - }, - "view_reads": { - "type": "long" - } - } - }, - "httpd_request_methods": { - "properties": { - "COPY": { - "type": "long" - }, - "DELETE": { - "type": "long" - }, - "GET": { - "type": "long" - }, - "HEAD": { - "type": "long" - }, - "POST": { - "type": "long" - }, - "PUT": { - "type": "long" - } - } - }, - "httpd_status_codes": { - "properties": { - "200": { - "type": "long" - }, - "201": { - "type": "long" - }, - "202": { - "type": "long" - }, - "301": { - "type": "long" - }, - "304": { - "type": "long" - }, - "400": { - "type": "long" - }, - "401": { - "type": "long" - }, - "403": { - "type": "long" - }, - "404": { - "type": "long" - }, - "405": { - "type": "long" - }, - "409": { - "type": "long" - }, - "412": { - "type": "long" - }, - "500": { - "type": "long" - } - } - } - } - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "command": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "ip_addresses": { - "type": "ip" - }, - "labels": { - "type": "object" - }, - "size": { - "properties": { - "root_fs": { - "type": "long" - }, - "rw": { - "type": "long" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cpu": { - "properties": { - "core": { - "properties": { - "*": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "object" - } - } - }, - "pct": { - "type": "object" - }, - "ticks": { - "type": "object" - } - } - } - } - }, - "kernel": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "system": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "user": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - } - } - }, - "diskio": { - "properties": { - "read": { - "properties": { - "bytes": { - "type": "long" - }, - "ops": { - "type": "long" - }, - "queued": { - "type": "long" - }, - "rate": { - "type": "long" - }, - "service_time": { - "type": "long" - }, - "wait_time": { - "type": "long" - } - } - }, - "reads": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "summary": { - "properties": { - "bytes": { - "type": "long" - }, - "ops": { - "type": "long" - }, - "queued": { - "type": "long" - }, - "rate": { - "type": "long" - }, - "service_time": { - "type": "long" - }, - "wait_time": { - "type": "long" - } - } - }, - "total": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "write": { - "properties": { - "bytes": { - "type": "long" - }, - "ops": { - "type": "long" - }, - "queued": { - "type": "long" - }, - "rate": { - "type": "long" - }, - "service_time": { - "type": "long" - }, - "wait_time": { - "type": "long" - } - } - }, - "writes": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "actor": { - "properties": { - "attributes": { - "type": "object" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "from": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "healthcheck": { - "properties": { - "event": { - "properties": { - "end_date": { - "type": "date" - }, - "exit_code": { - "type": "long" - }, - "output": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_date": { - "type": "date" - } - } - }, - "failingstreak": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "image": { - "properties": { - "created": { - "type": "date" - }, - "id": { - "properties": { - "current": { - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "size": { - "properties": { - "regular": { - "type": "long" - }, - "virtual": { - "type": "long" - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "info": { - "properties": { - "containers": { - "properties": { - "paused": { - "type": "long" - }, - "running": { - "type": "long" - }, - "stopped": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "images": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "commit": { - "properties": { - "peak": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "fail": { - "properties": { - "count": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "limit": { - "type": "long" - }, - "private_working_set": { - "properties": { - "total": { - "type": "long" - } - } - }, - "rss": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "total": { - "type": "long" - } - } - }, - "stats": { - "properties": { - "*": { - "type": "object" - } - } - }, - "usage": { - "properties": { - "max": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "total": { - "type": "long" - } - } - } - } - }, - "network": { - "properties": { - "in": { - "properties": { - "bytes": { - "type": "long" - }, - "dropped": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "errors": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - }, - "inbound": { - "properties": { - "bytes": { - "type": "long" - }, - "dropped": { - "type": "long" - }, - "errors": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - }, - "interface": { - "ignore_above": 1024, - "type": "keyword" - }, - "out": { - "properties": { - "bytes": { - "type": "long" - }, - "dropped": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "errors": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - }, - "outbound": { - "properties": { - "bytes": { - "type": "long" - }, - "dropped": { - "type": "long" - }, - "errors": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "elasticsearch": { - "properties": { - "ccr": { - "properties": { - "auto_follow": { - "properties": { - "failed": { - "properties": { - "follow_indices": { - "properties": { - "count": { - "type": "long" - } - } - }, - "remote_cluster_state_requests": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "success": { - "properties": { - "follow_indices": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "bytes_read": { - "type": "long" - }, - "follower": { - "properties": { - "aliases_version": { - "type": "long" - }, - "global_checkpoint": { - "type": "long" - }, - "index": { - "ignore_above": 1024, - "type": "keyword" - }, - "mapping_version": { - "type": "long" - }, - "max_seq_no": { - "type": "long" - }, - "operations": { - "properties": { - "read": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "operations_written": { - "type": "long" - }, - "settings_version": { - "type": "long" - }, - "shard": { - "properties": { - "number": { - "type": "long" - } - } - }, - "time_since_last_read": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "last_requested_seq_no": { - "type": "long" - }, - "leader": { - "properties": { - "global_checkpoint": { - "type": "long" - }, - "index": { - "ignore_above": 1024, - "type": "keyword" - }, - "max_seq_no": { - "type": "long" - } - } - }, - "read_exceptions": { - "type": "nested" - }, - "remote_cluster": { - "ignore_above": 1024, - "type": "keyword" - }, - "requests": { - "properties": { - "failed": { - "properties": { - "read": { - "properties": { - "count": { - "type": "long" - } - } - }, - "write": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "outstanding": { - "properties": { - "read": { - "properties": { - "count": { - "type": "long" - } - } - }, - "write": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "successful": { - "properties": { - "read": { - "properties": { - "count": { - "type": "long" - } - } - }, - "write": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "shard_id": { - "type": "long" - }, - "total_time": { - "properties": { - "read": { - "properties": { - "ms": { - "type": "long" - }, - "remote_exec": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "write_buffer": { - "properties": { - "operation": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "cluster": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "pending_task": { - "properties": { - "insert_order": { - "type": "long" - }, - "priority": { - "type": "long" - }, - "source": { - "ignore_above": 1024, - "type": "keyword" - }, - "time_in_queue": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "state": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stats": { - "properties": { - "indices": { - "properties": { - "fielddata": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "shards": { - "properties": { - "count": { - "type": "long" - }, - "docs": { - "properties": { - "total": { - "type": "long" - } - } - }, - "primaries": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "type": "long" - } - } - }, - "license": { - "properties": { - "expiry_date_in_millis": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "nodes": { - "properties": { - "count": { - "type": "long" - }, - "data": { - "type": "long" - }, - "fs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "jvm": { - "properties": { - "max_uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "master": { - "type": "long" - }, - "stats": { - "properties": { - "data": { - "type": "long" - } - } - } - } - }, - "stack": { - "properties": { - "apm": { - "properties": { - "found": { - "type": "boolean" - } - } - }, - "xpack": { - "properties": { - "ccr": { - "properties": { - "available": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - } - } - } - } - } - } - }, - "state": { - "properties": { - "master_node": { - "ignore_above": 1024, - "type": "keyword" - }, - "nodes_hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "state_uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "enrich": { - "properties": { - "executed_searches": { - "properties": { - "total": { - "type": "long" - } - } - }, - "executing_policy": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "task": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "cancellable": { - "type": "boolean" - }, - "id": { - "type": "long" - }, - "parent_task_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "task": { - "ignore_above": 1024, - "type": "keyword" - }, - "time": { - "properties": { - "running": { - "properties": { - "nano": { - "type": "long" - } - } - }, - "start": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "queue": { - "properties": { - "size": { - "type": "long" - } - } - }, - "remote_requests": { - "properties": { - "current": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "index": { - "properties": { - "created": { - "type": "long" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primaries": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "type": "long" - }, - "index_total": { - "type": "long" - }, - "throttle_time_in_millis": { - "type": "long" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "type": "long" - } - } - }, - "query_cache": { - "properties": { - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "refresh": { - "properties": { - "external_total_time_in_millis": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "request_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "type": "long" - }, - "query_total": { - "type": "long" - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values_memory_in_bytes": { - "type": "long" - }, - "fixed_bit_set_memory_in_bytes": { - "type": "long" - }, - "index_writer_memory_in_bytes": { - "type": "long" - }, - "memory_in_bytes": { - "type": "long" - }, - "norms_memory_in_bytes": { - "type": "long" - }, - "points_memory_in_bytes": { - "type": "long" - }, - "stored_fields_memory_in_bytes": { - "type": "long" - }, - "term_vectors_memory_in_bytes": { - "type": "long" - }, - "terms_memory_in_bytes": { - "type": "long" - }, - "version_map_memory_in_bytes": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - }, - "recovery": { - "properties": { - "id": { - "type": "long" - }, - "index": { - "properties": { - "files": { - "properties": { - "percent": { - "ignore_above": 1024, - "type": "keyword" - }, - "recovered": { - "type": "long" - }, - "reused": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "size": { - "properties": { - "recovered_in_bytes": { - "type": "long" - }, - "reused_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "primary": { - "type": "boolean" - }, - "source": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stage": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "stop_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "target": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "translog": { - "properties": { - "percent": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "type": "long" - }, - "total_on_start": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "verify_index": { - "properties": { - "check_index_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "total_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "shards": { - "properties": { - "total": { - "type": "long" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "summary": { - "properties": { - "primaries": { - "properties": { - "bulk": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "properties": { - "bytes": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - }, - "count": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "search": { - "properties": { - "query": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "total": { - "properties": { - "bulk": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "properties": { - "bytes": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "is_throttled": { - "type": "boolean" - }, - "throttle_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "query": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "total": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "evictions": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "type": "long" - }, - "index_total": { - "type": "long" - }, - "throttle_time_in_millis": { - "type": "long" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "type": "long" - } - } - }, - "query_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "refresh": { - "properties": { - "external_total_time_in_millis": { - "type": "long" - }, - "total_time_in_millis": { - "type": "long" - } - } - }, - "request_cache": { - "properties": { - "evictions": { - "type": "long" - }, - "hit_count": { - "type": "long" - }, - "memory_size_in_bytes": { - "type": "long" - }, - "miss_count": { - "type": "long" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "type": "long" - }, - "query_total": { - "type": "long" - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values_memory_in_bytes": { - "type": "long" - }, - "fixed_bit_set_memory_in_bytes": { - "type": "long" - }, - "index_writer_memory_in_bytes": { - "type": "long" - }, - "memory_in_bytes": { - "type": "long" - }, - "norms_memory_in_bytes": { - "type": "long" - }, - "points_memory_in_bytes": { - "type": "long" - }, - "stored_fields_memory_in_bytes": { - "type": "long" - }, - "term_vectors_memory_in_bytes": { - "type": "long" - }, - "terms_memory_in_bytes": { - "type": "long" - }, - "version_map_memory_in_bytes": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ml": { - "properties": { - "job": { - "properties": { - "data": { - "properties": { - "invalid_date": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "data_counts": { - "properties": { - "invalid_date_count": { - "type": "long" - }, - "processed_record_count": { - "type": "long" - } - } - }, - "forecasts_stats": { - "properties": { - "total": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "model_size": { - "properties": { - "memory_status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "jvm": { - "properties": { - "memory": { - "properties": { - "heap": { - "properties": { - "init": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "nonheap": { - "properties": { - "init": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "master": { - "type": "boolean" - }, - "mlockall": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "process": { - "properties": { - "mlockall": { - "type": "boolean" - } - } - }, - "stats": { - "properties": { - "fs": { - "properties": { - "io_stats": { - "properties": { - "total": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - }, - "read": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "operations": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "summary": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "free": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "available_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - } - } - } - } - }, - "indices": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - }, - "deleted": { - "type": "long" - } - } - }, - "fielddata": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "indexing": { - "properties": { - "index_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "index_total": { - "properties": { - "count": { - "type": "long" - } - } - }, - "throttle_time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "query_cache": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "request_cache": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "query_time": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "query_total": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "segments": { - "properties": { - "count": { - "type": "long" - }, - "doc_values": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "fixed_bit_set": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "index_writer": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "norms": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "points": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "stored_fields": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "term_vectors": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "terms": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "version_map": { - "properties": { - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - }, - "young": { - "properties": { - "collection": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "double" - } - } - } - } - }, - "pools": { - "properties": { - "old": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "survivor": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "young": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "peak_max": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs": { - "properties": { - "quota": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "stat": { - "properties": { - "elapsed_periods": { - "properties": { - "count": { - "type": "long" - } - } - }, - "time_throttled": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "times_throttled": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "control_group": { - "ignore_above": 1024, - "type": "keyword" - }, - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "cpu": { - "properties": { - "load_avg": { - "properties": { - "1m": { - "type": "half_float" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "pct": { - "type": "double" - } - } - } - } - }, - "thread_pool": { - "properties": { - "bulk": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "get": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "index": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "search": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "queue": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shard": { - "properties": { - "number": { - "type": "long" - }, - "primary": { - "type": "boolean" - }, - "relocating_node": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source_node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "envoyproxy": { - "properties": { - "server": { - "properties": { - "cluster_manager": { - "properties": { - "active_clusters": { - "type": "long" - }, - "cluster_added": { - "type": "long" - }, - "cluster_modified": { - "type": "long" - }, - "cluster_removed": { - "type": "long" - }, - "cluster_updated": { - "type": "long" - }, - "cluster_updated_via_merge": { - "type": "long" - }, - "update_merge_cancelled": { - "type": "long" - }, - "update_out_of_merge_window": { - "type": "long" - }, - "warming_clusters": { - "type": "long" - } - } - }, - "filesystem": { - "properties": { - "flushed_by_timer": { - "type": "long" - }, - "reopen_failed": { - "type": "long" - }, - "write_buffered": { - "type": "long" - }, - "write_completed": { - "type": "long" - }, - "write_failed": { - "type": "long" - }, - "write_total_buffered": { - "type": "long" - } - } - }, - "http2": { - "properties": { - "header_overflow": { - "type": "long" - }, - "headers_cb_no_stream": { - "type": "long" - }, - "rx_messaging_error": { - "type": "long" - }, - "rx_reset": { - "type": "long" - }, - "too_many_header_frames": { - "type": "long" - }, - "trailers": { - "type": "long" - }, - "tx_reset": { - "type": "long" - } - } - }, - "listener_manager": { - "properties": { - "listener_added": { - "type": "long" - }, - "listener_create_failure": { - "type": "long" - }, - "listener_create_success": { - "type": "long" - }, - "listener_modified": { - "type": "long" - }, - "listener_removed": { - "type": "long" - }, - "listener_stopped": { - "type": "long" - }, - "total_listeners_active": { - "type": "long" - }, - "total_listeners_draining": { - "type": "long" - }, - "total_listeners_warming": { - "type": "long" - } - } - }, - "runtime": { - "properties": { - "admin_overrides_active": { - "type": "long" - }, - "deprecated_feature_use": { - "type": "long" - }, - "load_error": { - "type": "long" - }, - "load_success": { - "type": "long" - }, - "num_keys": { - "type": "long" - }, - "num_layers": { - "type": "long" - }, - "override_dir_exists": { - "type": "long" - }, - "override_dir_not_exists": { - "type": "long" - } - } - }, - "server": { - "properties": { - "concurrency": { - "type": "long" - }, - "days_until_first_cert_expiring": { - "type": "long" - }, - "debug_assertion_failures": { - "type": "long" - }, - "dynamic_unknown_fields": { - "type": "long" - }, - "hot_restart_epoch": { - "type": "long" - }, - "live": { - "type": "long" - }, - "memory_allocated": { - "type": "long" - }, - "memory_heap_size": { - "type": "long" - }, - "parent_connections": { - "type": "long" - }, - "state": { - "type": "long" - }, - "static_unknown_fields": { - "type": "long" - }, - "stats_recent_lookups": { - "type": "long" - }, - "total_connections": { - "type": "long" - }, - "uptime": { - "type": "long" - }, - "version": { - "type": "long" - }, - "watchdog_mega_miss": { - "type": "long" - }, - "watchdog_miss": { - "type": "long" - } - } - }, - "stats": { - "properties": { - "overflow": { - "type": "long" - } - } - } - } - } - } - }, - "error": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "etcd": { - "properties": { - "api_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "disk": { - "properties": { - "backend_commit_duration": { - "properties": { - "ns": { - "properties": { - "bucket": { - "properties": { - "*": { - "type": "object" - } - } - }, - "count": { - "type": "long" - }, - "sum": { - "type": "long" - } - } - } - } - }, - "mvcc_db_total_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "wal_fsync_duration": { - "properties": { - "ns": { - "properties": { - "bucket": { - "properties": { - "*": { - "type": "object" - } - } - }, - "count": { - "type": "long" - }, - "sum": { - "type": "long" - } - } - } - } - } - } - }, - "leader": { - "properties": { - "followers": { - "properties": { - "counts": { - "properties": { - "followers": { - "properties": { - "counts": { - "properties": { - "fail": { - "type": "long" - }, - "success": { - "type": "long" - } - } - } - } - } - } - }, - "latency": { - "properties": { - "follower": { - "properties": { - "latency": { - "properties": { - "standardDeviation": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "followers": { - "properties": { - "latency": { - "properties": { - "average": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "current": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "maximum": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "minimum": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "leader": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "memory": { - "properties": { - "go_memstats_alloc": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "network": { - "properties": { - "client_grpc_received": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "client_grpc_sent": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "self": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "leaderinfo": { - "properties": { - "leader": { - "ignore_above": 1024, - "type": "keyword" - }, - "starttime": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "recv": { - "properties": { - "appendrequest": { - "properties": { - "count": { - "type": "long" - } - } - }, - "bandwidthrate": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "pkgrate": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "send": { - "properties": { - "appendrequest": { - "properties": { - "count": { - "type": "long" - } - } - }, - "bandwidthrate": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "pkgrate": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "starttime": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "grpc_handled": { - "properties": { - "count": { - "type": "long" - } - } - }, - "grpc_started": { - "properties": { - "count": { - "type": "long" - } - } - }, - "has_leader": { - "type": "byte" - }, - "leader_changes": { - "properties": { - "count": { - "type": "long" - } - } - }, - "proposals_committed": { - "properties": { - "count": { - "type": "long" - } - } - }, - "proposals_failed": { - "properties": { - "count": { - "type": "long" - } - } - }, - "proposals_pending": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "store": { - "properties": { - "compareanddelete": { - "properties": { - "fail": { - "type": "long" - }, - "success": { - "type": "long" - } - } - }, - "compareandswap": { - "properties": { - "fail": { - "type": "long" - }, - "success": { - "type": "long" - } - } - }, - "create": { - "properties": { - "fail": { - "type": "long" - }, - "success": { - "type": "long" - } - } - }, - "delete": { - "properties": { - "fail": { - "type": "long" - }, - "success": { - "type": "long" - } - } - }, - "expire": { - "properties": { - "count": { - "type": "long" - } - } - }, - "gets": { - "properties": { - "fail": { - "type": "long" - }, - "success": { - "type": "long" - } - } - }, - "sets": { - "properties": { - "fail": { - "type": "long" - }, - "success": { - "type": "long" - } - } - }, - "update": { - "properties": { - "fail": { - "type": "long" - }, - "success": { - "type": "long" - } - } - }, - "watchers": { - "type": "long" - } - } - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "golang": { - "properties": { - "expvar": { - "properties": { - "cmdline": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "active": { - "type": "long" - }, - "allocated": { - "type": "long" - }, - "frees": { - "type": "long" - }, - "idle": { - "type": "long" - }, - "mallocs": { - "type": "long" - }, - "objects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "cmdline": { - "ignore_above": 1024, - "type": "keyword" - }, - "gc": { - "properties": { - "cpu_fraction": { - "type": "float" - }, - "next_gc_limit": { - "type": "long" - }, - "pause": { - "properties": { - "avg": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "count": { - "type": "long" - }, - "max": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "sum": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "total_count": { - "type": "long" - }, - "total_pause": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "system": { - "properties": { - "obtained": { - "type": "long" - }, - "released": { - "type": "long" - }, - "stack": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - } - } - }, - "graphite": { - "properties": { - "server": { - "properties": { - "example": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "haproxy": { - "properties": { - "info": { - "properties": { - "busy_polling": { - "type": "long" - }, - "bytes": { - "properties": { - "out": { - "properties": { - "rate": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "compress": { - "properties": { - "bps": { - "properties": { - "in": { - "type": "long" - }, - "out": { - "type": "long" - }, - "rate_limit": { - "type": "long" - } - } - } - } - }, - "connection": { - "properties": { - "current": { - "type": "long" - }, - "hard_max": { - "type": "long" - }, - "max": { - "type": "long" - }, - "rate": { - "properties": { - "limit": { - "type": "long" - }, - "max": { - "type": "long" - }, - "value": { - "type": "long" - } - } - }, - "ssl": { - "properties": { - "current": { - "type": "long" - }, - "max": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "dropped_logs": { - "type": "long" - }, - "failed_resolutions": { - "type": "long" - }, - "idle": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "jobs": { - "type": "long" - }, - "listeners": { - "type": "long" - }, - "memory": { - "properties": { - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "peers": { - "properties": { - "active": { - "type": "long" - }, - "connected": { - "type": "long" - } - } - }, - "pipes": { - "properties": { - "free": { - "type": "long" - }, - "max": { - "type": "long" - }, - "used": { - "type": "long" - } - } - }, - "pool": { - "properties": { - "allocated": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "used": { - "type": "long" - } - } - }, - "process_num": { - "type": "long" - }, - "processes": { - "type": "long" - }, - "requests": { - "properties": { - "max": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "run_queue": { - "type": "long" - }, - "session": { - "properties": { - "rate": { - "properties": { - "limit": { - "type": "long" - }, - "max": { - "type": "long" - }, - "value": { - "type": "long" - } - } - } - } - }, - "sockets": { - "properties": { - "max": { - "type": "long" - } - } - }, - "ssl": { - "properties": { - "backend": { - "properties": { - "key_rate": { - "properties": { - "max": { - "type": "long" - }, - "value": { - "type": "long" - } - } - } - } - }, - "cache_misses": { - "type": "long" - }, - "cached_lookups": { - "type": "long" - }, - "frontend": { - "properties": { - "key_rate": { - "properties": { - "max": { - "type": "long" - }, - "value": { - "type": "long" - } - } - }, - "session_reuse": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "rate": { - "properties": { - "limit": { - "type": "long" - }, - "max": { - "type": "long" - }, - "value": { - "type": "long" - } - } - } - } - }, - "stopping": { - "type": "long" - }, - "tasks": { - "type": "long" - }, - "threads": { - "type": "long" - }, - "ulimit_n": { - "type": "long" - }, - "unstoppable_jobs": { - "type": "long" - }, - "uptime": { - "properties": { - "sec": { - "type": "long" - } - } - }, - "zlib_mem_usage": { - "properties": { - "max": { - "type": "long" - }, - "value": { - "type": "long" - } - } - } - } - }, - "stat": { - "properties": { - "agent": { - "properties": { - "check": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "fall": { - "type": "long" - }, - "health": { - "type": "long" - }, - "rise": { - "type": "long" - } - } - }, - "code": { - "type": "long" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "fall": { - "type": "long" - }, - "health": { - "type": "long" - }, - "rise": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "check": { - "properties": { - "agent": { - "properties": { - "last": { - "type": "long" - } - } - }, - "code": { - "type": "long" - }, - "down": { - "type": "long" - }, - "duration": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "health": { - "properties": { - "fail": { - "type": "long" - }, - "last": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "properties": { - "aborted": { - "type": "long" - } - } - }, - "component_type": { - "type": "long" - }, - "compressor": { - "properties": { - "bypassed": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "in": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "out": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "response": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "connection": { - "properties": { - "attempt": { - "properties": { - "total": { - "type": "long" - } - } - }, - "cache": { - "properties": { - "hits": { - "type": "long" - }, - "lookup": { - "properties": { - "total": { - "type": "long" - } - } - } - } - }, - "idle": { - "properties": { - "limit": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "rate": { - "type": "long" - }, - "rate_max": { - "type": "long" - }, - "retried": { - "type": "long" - }, - "reuse": { - "properties": { - "total": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "cookie": { - "ignore_above": 1024, - "type": "keyword" - }, - "downtime": { - "type": "long" - }, - "header": { - "properties": { - "rewrite": { - "properties": { - "failed": { - "properties": { - "total": { - "type": "long" - } - } - } - } - } - } - }, - "in": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "last_change": { - "type": "long" - }, - "load_balancing_algorithm": { - "ignore_above": 1024, - "type": "keyword" - }, - "out": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "proxy": { - "properties": { - "id": { - "type": "long" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "queue": { - "properties": { - "limit": { - "type": "long" - }, - "time": { - "properties": { - "avg": { - "type": "long" - } - } - } - } - }, - "request": { - "properties": { - "connection": { - "properties": { - "errors": { - "type": "long" - } - } - }, - "denied": { - "type": "long" - }, - "denied_by_connection_rules": { - "type": "long" - }, - "denied_by_session_rules": { - "type": "long" - }, - "errors": { - "type": "long" - }, - "intercepted": { - "type": "long" - }, - "queued": { - "properties": { - "current": { - "type": "long" - }, - "max": { - "type": "long" - } - } - }, - "rate": { - "properties": { - "max": { - "type": "long" - }, - "value": { - "type": "long" - } - } - }, - "redispatched": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "response": { - "properties": { - "denied": { - "type": "long" - }, - "errors": { - "type": "long" - }, - "http": { - "properties": { - "1xx": { - "type": "long" - }, - "2xx": { - "type": "long" - }, - "3xx": { - "type": "long" - }, - "4xx": { - "type": "long" - }, - "5xx": { - "type": "long" - }, - "other": { - "type": "long" - } - } - }, - "time": { - "properties": { - "avg": { - "type": "long" - } - } - } - } - }, - "selected": { - "properties": { - "total": { - "type": "long" - } - } - }, - "server": { - "properties": { - "aborted": { - "type": "long" - }, - "active": { - "type": "long" - }, - "backup": { - "type": "long" - }, - "id": { - "type": "long" - } - } - }, - "service_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "session": { - "properties": { - "current": { - "type": "long" - }, - "limit": { - "type": "long" - }, - "max": { - "type": "long" - }, - "rate": { - "properties": { - "limit": { - "type": "long" - }, - "max": { - "type": "long" - }, - "value": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "source": { - "properties": { - "address": { - "norms": false, - "type": "text" - } - } - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "throttle": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "tracked": { - "properties": { - "id": { - "type": "long" - } - } - }, - "weight": { - "type": "long" - } - } - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "cpu": { - "properties": { - "pct": { - "type": "long" - } - } - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "network": { - "properties": { - "in": { - "properties": { - "bytes": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - }, - "out": { - "properties": { - "bytes": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - } - } - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "headers": { - "type": "object" - }, - "phrase": { - "ignore_above": 1024, - "type": "keyword" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index_recovery": { - "properties": { - "shards": { - "properties": { - "start_time_in_millis": { - "path": "elasticsearch.index.recovery.start_time.ms", - "type": "alias" - }, - "stop_time_in_millis": { - "path": "elasticsearch.index.recovery.stop_time.ms", - "type": "alias" - } - } - } - } - }, - "index_stats": { - "properties": { - "index": { - "path": "elasticsearch.index.name", - "type": "alias" - }, - "primaries": { - "properties": { - "docs": { - "properties": { - "count": { - "path": "elasticsearch.index.primaries.docs.count", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.primaries.indexing.index_time_in_millis", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.primaries.indexing.index_total", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.index.primaries.indexing.throttle_time_in_millis", - "type": "alias" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "path": "elasticsearch.index.primaries.merges.total_size_in_bytes", - "type": "alias" - } - } - }, - "refresh": { - "properties": { - "total_time_in_millis": { - "path": "elasticsearch.index.primaries.refresh.total_time_in_millis", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.index.primaries.segments.count", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "path": "elasticsearch.index.primaries.store.size_in_bytes", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "fielddata": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.fielddata.memory_size_in_bytes", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.total.indexing.index_time_in_millis", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.total.indexing.index_total", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.index.total.indexing.throttle_time_in_millis", - "type": "alias" - } - } - }, - "merges": { - "properties": { - "total_size_in_bytes": { - "path": "elasticsearch.index.total.merges.total_size_in_bytes", - "type": "alias" - } - } - }, - "query_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.query_cache.memory_size_in_bytes", - "type": "alias" - } - } - }, - "refresh": { - "properties": { - "total_time_in_millis": { - "path": "elasticsearch.index.total.refresh.total_time_in_millis", - "type": "alias" - } - } - }, - "request_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.index.total.request_cache.memory_size_in_bytes", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.index.total.search.query_time_in_millis", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.index.total.search.query_total", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.index.total.segments.count", - "type": "alias" - }, - "doc_values_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.doc_values_memory_in_bytes", - "type": "alias" - }, - "fixed_bit_set_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.fixed_bit_set_memory_in_bytes", - "type": "alias" - }, - "index_writer_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.index_writer_memory_in_bytes", - "type": "alias" - }, - "memory_in_bytes": { - "path": "elasticsearch.index.total.segments.memory_in_bytes", - "type": "alias" - }, - "norms_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.norms_memory_in_bytes", - "type": "alias" - }, - "points_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.points_memory_in_bytes", - "type": "alias" - }, - "stored_fields_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.stored_fields_memory_in_bytes", - "type": "alias" - }, - "term_vectors_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.term_vectors_memory_in_bytes", - "type": "alias" - }, - "terms_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.terms_memory_in_bytes", - "type": "alias" - }, - "version_map_memory_in_bytes": { - "path": "elasticsearch.index.total.segments.version_map_memory_in_bytes", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "path": "elasticsearch.index.total.store.size_in_bytes", - "type": "alias" - } - } - } - } - } - } - }, - "indices_stats": { - "properties": { - "_all": { - "properties": { - "primaries": { - "properties": { - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.index.summary.primaries.indexing.index.time.ms", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.index.summary.primaries.indexing.index.count", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "indexing": { - "properties": { - "index_total": { - "path": "elasticsearch.index.summary.total.indexing.index.count", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.index.summary.total.search.query.time.ms", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.index.summary.total.search.query.count", - "type": "alias" - } - } - } - } - } - } - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "job_stats": { - "properties": { - "forecasts_stats": { - "properties": { - "total": { - "path": "elasticsearch.ml.job.forecasts_stats.total", - "type": "alias" - } - } - }, - "job_id": { - "path": "elasticsearch.ml.job.id", - "type": "alias" - } - } - }, - "jolokia": { - "properties": { - "agent": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "secured": { - "type": "boolean" - }, - "server": { - "properties": { - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kafka": { - "properties": { - "broker": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - }, - "log": { - "properties": { - "flush_rate": { - "type": "float" - } - } - }, - "mbean": { - "ignore_above": 1024, - "type": "keyword" - }, - "messages_in": { - "type": "float" - }, - "net": { - "properties": { - "in": { - "properties": { - "bytes_per_sec": { - "type": "float" - } - } - }, - "out": { - "properties": { - "bytes_per_sec": { - "type": "float" - } - } - }, - "rejected": { - "properties": { - "bytes_per_sec": { - "type": "float" - } - } - } - } - }, - "replication": { - "properties": { - "leader_elections": { - "type": "float" - }, - "unclean_leader_elections": { - "type": "float" - } - } - }, - "request": { - "properties": { - "channel": { - "properties": { - "queue": { - "properties": { - "size": { - "type": "long" - } - } - } - } - }, - "fetch": { - "properties": { - "failed": { - "type": "float" - }, - "failed_per_second": { - "type": "float" - } - } - }, - "produce": { - "properties": { - "failed": { - "type": "float" - }, - "failed_per_second": { - "type": "float" - } - } - } - } - }, - "session": { - "properties": { - "zookeeper": { - "properties": { - "disconnect": { - "type": "float" - }, - "expire": { - "type": "float" - }, - "readonly": { - "type": "float" - }, - "sync": { - "type": "float" - } - } - } - } - }, - "topic": { - "properties": { - "messages_in": { - "type": "float" - }, - "net": { - "properties": { - "in": { - "properties": { - "bytes_per_sec": { - "type": "float" - } - } - }, - "out": { - "properties": { - "bytes_per_sec": { - "type": "float" - } - } - }, - "rejected": { - "properties": { - "bytes_per_sec": { - "type": "float" - } - } - } - } - } - } - } - } - }, - "consumer": { - "properties": { - "bytes_consumed": { - "type": "float" - }, - "fetch_rate": { - "type": "float" - }, - "in": { - "properties": { - "bytes_per_sec": { - "type": "float" - } - } - }, - "kafka_commits": { - "type": "float" - }, - "max_lag": { - "type": "float" - }, - "mbean": { - "ignore_above": 1024, - "type": "keyword" - }, - "messages_in": { - "type": "float" - }, - "records_consumed": { - "type": "float" - }, - "zookeeper_commits": { - "type": "float" - } - } - }, - "consumergroup": { - "properties": { - "broker": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - } - } - }, - "client": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "member_id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "consumer_lag": { - "type": "long" - }, - "error": { - "properties": { - "code": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "meta": { - "ignore_above": 1024, - "type": "keyword" - }, - "offset": { - "type": "long" - }, - "partition": { - "type": "long" - }, - "topic": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "partition": { - "properties": { - "broker": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "type": "long" - } - } - }, - "id": { - "type": "long" - }, - "offset": { - "properties": { - "newest": { - "type": "long" - }, - "oldest": { - "type": "long" - } - } - }, - "partition": { - "properties": { - "error": { - "properties": { - "code": { - "type": "long" - } - } - }, - "id": { - "type": "long" - }, - "insync_replica": { - "type": "boolean" - }, - "is_leader": { - "type": "boolean" - }, - "leader": { - "type": "long" - }, - "replica": { - "type": "long" - } - } - }, - "topic": { - "properties": { - "error": { - "properties": { - "code": { - "type": "long" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "topic_broker_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "topic_id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "producer": { - "properties": { - "available_buffer_bytes": { - "type": "float" - }, - "batch_size_avg": { - "type": "float" - }, - "batch_size_max": { - "type": "long" - }, - "io_wait": { - "type": "float" - }, - "mbean": { - "ignore_above": 1024, - "type": "keyword" - }, - "message_rate": { - "type": "float" - }, - "out": { - "properties": { - "bytes_per_sec": { - "type": "float" - } - } - }, - "record_error_rate": { - "type": "float" - }, - "record_retry_rate": { - "type": "float" - }, - "record_send_rate": { - "type": "float" - }, - "record_size_avg": { - "type": "float" - }, - "record_size_max": { - "type": "long" - }, - "records_per_request": { - "type": "float" - }, - "request_rate": { - "type": "float" - }, - "response_rate": { - "type": "float" - } - } - }, - "topic": { - "properties": { - "error": { - "properties": { - "code": { - "type": "long" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "kibana": { - "properties": { - "settings": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "index": { - "ignore_above": 1024, - "type": "keyword" - }, - "locale": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stats": { - "properties": { - "concurrent_connections": { - "type": "long" - }, - "host": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "index": { - "ignore_above": 1024, - "type": "keyword" - }, - "kibana": { - "properties": { - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "distro": { - "ignore_above": 1024, - "type": "keyword" - }, - "distroRelease": { - "ignore_above": 1024, - "type": "keyword" - }, - "load": { - "properties": { - "15m": { - "type": "half_float" - }, - "1m": { - "type": "half_float" - }, - "5m": { - "type": "half_float" - } - } - }, - "memory": { - "properties": { - "free_in_bytes": { - "type": "long" - }, - "total_in_bytes": { - "type": "long" - }, - "used_in_bytes": { - "type": "long" - } - } - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "platformRelease": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "event_loop_delay": { - "properties": { - "ms": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "size_limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "resident_set_size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "request": { - "properties": { - "disconnects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "response_time": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "max": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "snapshot": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "usage": { - "properties": { - "index": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "status": { - "properties": { - "metrics": { - "properties": { - "concurrent_connections": { - "type": "long" - }, - "requests": { - "properties": { - "disconnects": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "properties": { - "overall": { - "properties": { - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "kibana_stats": { - "properties": { - "concurrent_connections": { - "path": "kibana.stats.concurrent_connections", - "type": "alias" - }, - "kibana": { - "properties": { - "response_time": { - "properties": { - "max": { - "path": "kibana.stats.response_time.max.ms", - "type": "alias" - } - } - }, - "status": { - "path": "kibana.stats.kibana.status", - "type": "alias" - }, - "uuid": { - "path": "service.id", - "type": "alias" - } - } - }, - "os": { - "properties": { - "load": { - "properties": { - "15m": { - "path": "kibana.stats.os.load.15m", - "type": "alias" - }, - "1m": { - "path": "kibana.stats.os.load.1m", - "type": "alias" - }, - "5m": { - "path": "kibana.stats.os.load.5m", - "type": "alias" - } - } - }, - "memory": { - "properties": { - "free_in_bytes": { - "path": "kibana.stats.os.memory.free_in_bytes", - "type": "alias" - } - } - } - } - }, - "process": { - "properties": { - "event_loop_delay": { - "path": "kibana.stats.process.event_loop_delay.ms", - "type": "alias" - }, - "memory": { - "properties": { - "heap": { - "properties": { - "size_limit": { - "path": "kibana.stats.process.memory.heap.size_limit.bytes", - "type": "alias" - } - } - }, - "resident_set_size_in_bytes": { - "path": "kibana.stats.process.memory.resident_set_size.bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "kibana.stats.process.uptime.ms", - "type": "alias" - } - } - }, - "requests": { - "properties": { - "disconnects": { - "path": "kibana.stats.request.disconnects", - "type": "alias" - }, - "total": { - "path": "kibana.stats.request.total", - "type": "alias" - } - } - }, - "response_times": { - "properties": { - "average": { - "path": "kibana.stats.response_time.avg.ms", - "type": "alias" - }, - "max": { - "path": "kibana.stats.response_time.max.ms", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "kubernetes": { - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "apiserver": { - "properties": { - "audit": { - "properties": { - "event": { - "properties": { - "count": { - "type": "long" - } - } - }, - "rejected": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "client": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "etcd": { - "properties": { - "object": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - }, - "duration": { - "properties": { - "us": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "double" - } - } - } - } - }, - "size": { - "properties": { - "bytes": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "long" - } - } - } - } - } - } - }, - "response": { - "properties": { - "size": { - "properties": { - "bytes": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "fds": { - "properties": { - "open": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "resident": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "virtual": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "started": { - "properties": { - "sec": { - "type": "double" - } - } - } - } - }, - "request": { - "properties": { - "client": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "component": { - "ignore_above": 1024, - "type": "keyword" - }, - "content_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "count": { - "type": "long" - }, - "current": { - "properties": { - "count": { - "type": "long" - } - } - }, - "dry_run": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "properties": { - "us": { - "properties": { - "bucket": { - "properties": { - "*": { - "type": "object" - } - } - }, - "count": { - "type": "long" - }, - "sum": { - "type": "long" - } - } - } - } - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "handler": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "latency": { - "properties": { - "bucket": { - "properties": { - "*": { - "type": "object" - } - } - }, - "count": { - "type": "long" - }, - "sum": { - "type": "long" - } - } - }, - "longrunning": { - "properties": { - "count": { - "type": "long" - } - } - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "subresource": { - "ignore_above": 1024, - "type": "keyword" - }, - "verb": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "container": { - "properties": { - "cpu": { - "properties": { - "limit": { - "properties": { - "cores": { - "type": "float" - }, - "nanocores": { - "type": "long" - } - } - }, - "request": { - "properties": { - "cores": { - "type": "float" - }, - "nanocores": { - "type": "long" - } - } - }, - "usage": { - "properties": { - "core": { - "properties": { - "ns": { - "type": "double" - } - } - }, - "limit": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "nanocores": { - "type": "double" - }, - "node": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "logs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "capacity": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "inodes": { - "properties": { - "count": { - "type": "double" - }, - "free": { - "type": "double" - }, - "used": { - "type": "double" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "double" - } - } - } - } - }, - "memory": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "majorpagefaults": { - "type": "double" - }, - "pagefaults": { - "type": "double" - }, - "request": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "rss": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "double" - }, - "limit": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "node": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "workingset": { - "properties": { - "bytes": { - "type": "double" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "rootfs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "capacity": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "inodes": { - "properties": { - "used": { - "type": "double" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "double" - } - } - } - } - }, - "start_time": { - "type": "date" - }, - "status": { - "properties": { - "phase": { - "ignore_above": 1024, - "type": "keyword" - }, - "ready": { - "type": "boolean" - }, - "reason": { - "ignore_above": 1024, - "type": "keyword" - }, - "restarts": { - "type": "long" - } - } - } - } - }, - "controllermanager": { - "properties": { - "client": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handler": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "http": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - }, - "duration": { - "properties": { - "us": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "double" - } - } - } - } - }, - "size": { - "properties": { - "bytes": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "long" - } - } - } - } - } - } - }, - "response": { - "properties": { - "size": { - "properties": { - "bytes": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "leader": { - "properties": { - "is_master": { - "type": "boolean" - } - } - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "collector": { - "properties": { - "count": { - "type": "long" - }, - "eviction": { - "properties": { - "count": { - "type": "long" - } - } - }, - "health": { - "properties": { - "pct": { - "type": "long" - } - } - }, - "unhealthy": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "fds": { - "properties": { - "open": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "resident": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "virtual": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "started": { - "properties": { - "sec": { - "type": "double" - } - } - } - } - }, - "workqueue": { - "properties": { - "adds": { - "properties": { - "count": { - "type": "long" - } - } - }, - "depth": { - "properties": { - "count": { - "type": "long" - } - } - }, - "longestrunning": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "retries": { - "properties": { - "count": { - "type": "long" - } - } - }, - "unfinished": { - "properties": { - "sec": { - "type": "double" - } - } - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cronjob": { - "properties": { - "active": { - "properties": { - "count": { - "type": "long" - } - } - }, - "concurrency": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "deadline": { - "properties": { - "sec": { - "type": "long" - } - } - }, - "is_suspended": { - "type": "boolean" - }, - "last_schedule": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "next_schedule": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "schedule": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "daemonset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "replicas": { - "properties": { - "available": { - "type": "long" - }, - "desired": { - "type": "long" - }, - "ready": { - "type": "long" - }, - "unavailable": { - "type": "long" - } - } - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "paused": { - "type": "boolean" - }, - "replicas": { - "properties": { - "available": { - "type": "long" - }, - "desired": { - "type": "long" - }, - "unavailable": { - "type": "long" - }, - "updated": { - "type": "long" - } - } - } - } - }, - "event": { - "properties": { - "count": { - "type": "long" - }, - "involved_object": { - "properties": { - "api_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "copy_to": [ - "message" - ], - "norms": false, - "type": "text" - }, - "metadata": { - "properties": { - "generate_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_link": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp": { - "properties": { - "created": { - "type": "date" - } - } - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "reason": { - "ignore_above": 1024, - "type": "keyword" - }, - "source": { - "properties": { - "component": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "first_occurrence": { - "type": "date" - }, - "last_occurrence": { - "type": "date" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "cpu": { - "properties": { - "allocatable": { - "properties": { - "cores": { - "type": "float" - } - } - }, - "capacity": { - "properties": { - "cores": { - "type": "long" - } - } - }, - "usage": { - "properties": { - "core": { - "properties": { - "ns": { - "type": "double" - } - } - }, - "nanocores": { - "type": "double" - } - } - } - } - }, - "fs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "capacity": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "inodes": { - "properties": { - "count": { - "type": "double" - }, - "free": { - "type": "double" - }, - "used": { - "type": "double" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "double" - } - } - } - } - }, - "memory": { - "properties": { - "allocatable": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "available": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "capacity": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "majorpagefaults": { - "type": "double" - }, - "pagefaults": { - "type": "double" - }, - "rss": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "workingset": { - "properties": { - "bytes": { - "type": "double" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "network": { - "properties": { - "rx": { - "properties": { - "bytes": { - "type": "double" - }, - "errors": { - "type": "double" - } - } - }, - "tx": { - "properties": { - "bytes": { - "type": "double" - }, - "errors": { - "type": "double" - } - } - } - } - }, - "pod": { - "properties": { - "allocatable": { - "properties": { - "total": { - "type": "long" - } - } - }, - "capacity": { - "properties": { - "total": { - "type": "long" - } - } - } - } - }, - "runtime": { - "properties": { - "imagefs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "capacity": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "double" - } - } - } - } - } - } - }, - "start_time": { - "type": "date" - }, - "status": { - "properties": { - "disk_pressure": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_pressure": { - "ignore_above": 1024, - "type": "keyword" - }, - "out_of_disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "pid_pressure": { - "ignore_above": 1024, - "type": "keyword" - }, - "ready": { - "ignore_above": 1024, - "type": "keyword" - }, - "unschedulable": { - "type": "boolean" - } - } - } - } - }, - "persistentvolume": { - "properties": { - "capacity": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "phase": { - "ignore_above": 1024, - "type": "keyword" - }, - "storage_class": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "persistentvolumeclaim": { - "properties": { - "access_mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "phase": { - "ignore_above": 1024, - "type": "keyword" - }, - "request_storage": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "storage_class": { - "ignore_above": 1024, - "type": "keyword" - }, - "volume_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "cpu": { - "properties": { - "usage": { - "properties": { - "limit": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "nanocores": { - "type": "double" - }, - "node": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "host_ip": { - "type": "ip" - }, - "ip": { - "type": "ip" - }, - "memory": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "major_page_faults": { - "type": "double" - }, - "page_faults": { - "type": "double" - }, - "rss": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "double" - }, - "limit": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "node": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "working_set": { - "properties": { - "bytes": { - "type": "double" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "network": { - "properties": { - "rx": { - "properties": { - "bytes": { - "type": "double" - }, - "errors": { - "type": "double" - } - } - }, - "tx": { - "properties": { - "bytes": { - "type": "double" - }, - "errors": { - "type": "double" - } - } - } - } - }, - "start_time": { - "type": "date" - }, - "status": { - "properties": { - "phase": { - "ignore_above": 1024, - "type": "keyword" - }, - "ready": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheduled": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "proxy": { - "properties": { - "client": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handler": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "http": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - }, - "duration": { - "properties": { - "us": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "double" - } - } - } - } - }, - "size": { - "properties": { - "bytes": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "long" - } - } - } - } - } - } - }, - "response": { - "properties": { - "size": { - "properties": { - "bytes": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "fds": { - "properties": { - "open": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "resident": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "virtual": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "started": { - "properties": { - "sec": { - "type": "double" - } - } - } - } - }, - "sync": { - "properties": { - "networkprogramming": { - "properties": { - "duration": { - "properties": { - "us": { - "properties": { - "bucket": { - "properties": { - "*": { - "type": "object" - } - } - }, - "count": { - "type": "long" - }, - "sum": { - "type": "long" - } - } - } - } - } - } - }, - "rules": { - "properties": { - "duration": { - "properties": { - "us": { - "properties": { - "bucket": { - "properties": { - "*": { - "type": "object" - } - } - }, - "count": { - "type": "long" - }, - "sum": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "replicas": { - "properties": { - "available": { - "type": "long" - }, - "desired": { - "type": "long" - }, - "labeled": { - "type": "long" - }, - "observed": { - "type": "long" - }, - "ready": { - "type": "long" - } - } - } - } - }, - "resourcequota": { - "properties": { - "created": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "quota": { - "type": "double" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "scheduler": { - "properties": { - "client": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handler": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "http": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - }, - "duration": { - "properties": { - "us": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "double" - } - } - } - } - }, - "size": { - "properties": { - "bytes": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "long" - } - } - } - } - } - } - }, - "response": { - "properties": { - "size": { - "properties": { - "bytes": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "leader": { - "properties": { - "is_master": { - "type": "boolean" - } - } - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "operation": { - "ignore_above": 1024, - "type": "keyword" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "sec": { - "type": "double" - } - } - }, - "fds": { - "properties": { - "open": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "resident": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "virtual": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "started": { - "properties": { - "sec": { - "type": "double" - } - } - } - } - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheduling": { - "properties": { - "duration": { - "properties": { - "seconds": { - "properties": { - "count": { - "type": "long" - }, - "percentile": { - "properties": { - "*": { - "type": "object" - } - } - }, - "sum": { - "type": "double" - } - } - } - } - }, - "e2e": { - "properties": { - "duration": { - "properties": { - "us": { - "properties": { - "bucket": { - "properties": { - "*": { - "type": "object" - } - } - }, - "count": { - "type": "long" - }, - "sum": { - "type": "long" - } - } - } - } - } - } - }, - "pod": { - "properties": { - "attempts": { - "properties": { - "count": { - "type": "long" - } - } - }, - "preemption": { - "properties": { - "victims": { - "properties": { - "bucket": { - "properties": { - "*": { - "type": "long" - } - } - }, - "count": { - "type": "long" - }, - "sum": { - "type": "long" - } - } - } - } - } - } - } - } - } - } - }, - "service": { - "properties": { - "cluster_ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "external_ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "external_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress_hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress_ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "load_balancer_ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "created": { - "type": "long" - }, - "generation": { - "properties": { - "desired": { - "type": "long" - }, - "observed": { - "type": "long" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "replicas": { - "properties": { - "desired": { - "type": "long" - }, - "observed": { - "type": "long" - } - } - } - } - }, - "storageclass": { - "properties": { - "created": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "provisioner": { - "ignore_above": 1024, - "type": "keyword" - }, - "reclaim_policy": { - "ignore_above": 1024, - "type": "keyword" - }, - "volume_binding_mode": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "container": { - "ignore_above": 1024, - "type": "keyword" - }, - "cpu": { - "properties": { - "usage": { - "properties": { - "core": { - "properties": { - "ns": { - "type": "double" - } - } - }, - "nanocores": { - "type": "double" - } - } - } - } - }, - "memory": { - "properties": { - "majorpagefaults": { - "type": "double" - }, - "pagefaults": { - "type": "double" - }, - "rss": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "workingset": { - "properties": { - "bytes": { - "type": "double" - } - } - } - } - }, - "start_time": { - "type": "date" - } - } - }, - "volume": { - "properties": { - "fs": { - "properties": { - "available": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "capacity": { - "properties": { - "bytes": { - "type": "double" - } - } - }, - "inodes": { - "properties": { - "count": { - "type": "double" - }, - "free": { - "type": "double" - }, - "used": { - "type": "double" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "double" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "kvm": { - "properties": { - "dommemstat": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "stat": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "type": "long" - } - } - } - } - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "properties": { - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "type": "object" - }, - "license": { - "properties": { - "status": { - "path": "elasticsearch.cluster.stats.license.status", - "type": "alias" - }, - "type": { - "path": "elasticsearch.cluster.stats.license.type", - "type": "alias" - } - } - }, - "linux": { - "properties": { - "conntrack": { - "properties": { - "summary": { - "properties": { - "drop": { - "type": "long" - }, - "early_drop": { - "type": "long" - }, - "entries": { - "type": "long" - }, - "found": { - "type": "long" - }, - "ignore": { - "type": "long" - }, - "insert_failed": { - "type": "long" - }, - "invalid": { - "type": "long" - }, - "search_restart": { - "type": "long" - } - } - } - } - }, - "iostat": { - "properties": { - "await": { - "type": "float" - }, - "busy": { - "type": "float" - }, - "queue": { - "properties": { - "avg_size": { - "type": "float" - } - } - }, - "read": { - "properties": { - "await": { - "type": "float" - }, - "per_sec": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "request": { - "properties": { - "merges_per_sec": { - "type": "float" - }, - "per_sec": { - "type": "float" - } - } - } - } - }, - "request": { - "properties": { - "avg_size": { - "type": "float" - } - } - }, - "service_time": { - "type": "float" - }, - "write": { - "properties": { - "await": { - "type": "float" - }, - "per_sec": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "request": { - "properties": { - "merges_per_sec": { - "type": "float" - }, - "per_sec": { - "type": "float" - } - } - } - } - } - } - }, - "ksm": { - "properties": { - "stats": { - "properties": { - "full_scans": { - "type": "long" - }, - "pages_shared": { - "type": "long" - }, - "pages_sharing": { - "type": "long" - }, - "pages_unshared": { - "type": "long" - }, - "stable_node_chains": { - "type": "long" - }, - "stable_node_dups": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "hugepages": { - "properties": { - "default_size": { - "type": "long" - }, - "free": { - "type": "long" - }, - "reserved": { - "type": "long" - }, - "surplus": { - "type": "long" - }, - "total": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "long" - } - } - } - } - }, - "page_stats": { - "properties": { - "direct_efficiency": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "kswapd_efficiency": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pgfree": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "pgscan_direct": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "pgscan_kswapd": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "pgsteal_direct": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "pgsteal_kswapd": { - "properties": { - "pages": { - "type": "long" - } - } - } - } - } - } - }, - "pageinfo": { - "properties": { - "buddy_info": { - "properties": { - "DMA": { - "properties": { - "0": { - "type": "long" - }, - "1": { - "type": "long" - }, - "10": { - "type": "long" - }, - "2": { - "type": "long" - }, - "3": { - "type": "long" - }, - "4": { - "type": "long" - }, - "5": { - "type": "long" - }, - "6": { - "type": "long" - }, - "7": { - "type": "long" - }, - "8": { - "type": "long" - }, - "9": { - "type": "long" - } - } - } - } - }, - "nodes": { - "properties": { - "*": { - "type": "object" - } - } - } - } - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "logstash": { - "properties": { - "node": { - "properties": { - "jvm": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "filtered": { - "type": "long" - }, - "in": { - "type": "long" - }, - "out": { - "type": "long" - } - } - }, - "jvm": { - "properties": { - "mem": { - "properties": { - "heap_max_in_bytes": { - "type": "long" - }, - "heap_used_in_bytes": { - "type": "long" - } - } - }, - "uptime_in_millis": { - "type": "long" - } - } - }, - "logstash": { - "properties": { - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "stat": { - "properties": { - "number_of_elapsed_periods": { - "type": "long" - }, - "number_of_times_throttled": { - "type": "long" - }, - "time_throttled_nanos": { - "type": "long" - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage_nanos": { - "type": "long" - } - } - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "type": "long" - }, - "1m": { - "type": "long" - }, - "5m": { - "type": "long" - } - } - } - } - } - } - }, - "pipelines": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "out": { - "type": "long" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - }, - "max_queue_size_in_bytes": { - "type": "long" - }, - "queue_size_in_bytes": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vertices": { - "properties": { - "duration_in_millis": { - "type": "long" - }, - "events_in": { - "type": "long" - }, - "events_out": { - "type": "long" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "pipeline_ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "queue_push_duration_in_millis": { - "type": "float" - } - } - } - }, - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "type": "double" - } - } - } - } - }, - "queue": { - "properties": { - "events_count": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "logstash_state": { - "properties": { - "pipeline": { - "properties": { - "hash": { - "path": "logstash.node.state.pipeline.hash", - "type": "alias" - }, - "id": { - "path": "logstash.node.state.pipeline.id", - "type": "alias" - } - } - } - } - }, - "logstash_stats": { - "properties": { - "events": { - "properties": { - "duration_in_millis": { - "path": "logstash.node.stats.events.duration_in_millis", - "type": "alias" - }, - "in": { - "path": "logstash.node.stats.events.in", - "type": "alias" - }, - "out": { - "path": "logstash.node.stats.events.out", - "type": "alias" - } - } - }, - "jvm": { - "properties": { - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_max_in_bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "logstash.node.stats.jvm.mem.heap_used_in_bytes", - "type": "alias" - } - } - }, - "uptime_in_millis": { - "path": "logstash.node.stats.jvm.uptime_in_millis", - "type": "alias" - } - } - }, - "logstash": { - "properties": { - "uuid": { - "path": "logstash.node.stats.logstash.uuid", - "type": "alias" - }, - "version": { - "path": "logstash.node.stats.logstash.version", - "type": "alias" - } - } - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpuacct": { - "properties": { - "usage_nanos": { - "path": "logstash.node.stats.os.cgroup.cpuacct.usage_nanos", - "type": "alias" - } - } - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "15m": { - "path": "logstash.node.stats.os.cpu.load_average.15m", - "type": "alias" - }, - "1m": { - "path": "logstash.node.stats.os.cpu.load_average.1m", - "type": "alias" - }, - "5m": { - "path": "logstash.node.stats.os.cpu.load_average.5m", - "type": "alias" - } - } - }, - "stat": { - "properties": { - "number_of_elapsed_periods": { - "path": "logstash.node.stats.os.cgroup.cpu.stat.number_of_elapsed_periods", - "type": "alias" - }, - "number_of_times_throttled": { - "path": "logstash.node.stats.os.cgroup.cpu.stat.number_of_times_throttled", - "type": "alias" - }, - "time_throttled_nanos": { - "path": "logstash.node.stats.os.cgroup.cpu.stat.time_throttled_nanos", - "type": "alias" - } - } - } - } - } - } - }, - "pipelines": { - "type": "nested" - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "logstash.node.stats.process.cpu.percent", - "type": "alias" - } - } - } - } - }, - "queue": { - "properties": { - "events_count": { - "path": "logstash.node.stats.queue.events_count", - "type": "alias" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - } - } - }, - "memcached": { - "properties": { - "stats": { - "properties": { - "bytes": { - "properties": { - "current": { - "type": "long" - }, - "limit": { - "type": "long" - } - } - }, - "cmd": { - "properties": { - "get": { - "type": "long" - }, - "set": { - "type": "long" - } - } - }, - "connections": { - "properties": { - "current": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "evictions": { - "type": "long" - }, - "get": { - "properties": { - "hits": { - "type": "long" - }, - "misses": { - "type": "long" - } - } - }, - "items": { - "properties": { - "current": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "pid": { - "type": "long" - }, - "read": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "threads": { - "type": "long" - }, - "uptime": { - "properties": { - "sec": { - "type": "long" - } - } - }, - "written": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "metricset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "period": { - "type": "long" - } - } - }, - "mongodb": { - "properties": { - "collstats": { - "properties": { - "collection": { - "ignore_above": 1024, - "type": "keyword" - }, - "commands": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "db": { - "ignore_above": 1024, - "type": "keyword" - }, - "getmore": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "insert": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "lock": { - "properties": { - "read": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "write": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "queries": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "remove": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "update": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - } - } - }, - "dbstats": { - "properties": { - "avg_obj_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "collections": { - "type": "long" - }, - "data_file_version": { - "properties": { - "major": { - "type": "long" - }, - "minor": { - "type": "long" - } - } - }, - "data_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "db": { - "ignore_above": 1024, - "type": "keyword" - }, - "extent_free_list": { - "properties": { - "num": { - "type": "long" - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "file_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "index_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "indexes": { - "type": "long" - }, - "ns_size_mb": { - "properties": { - "mb": { - "type": "long" - } - } - }, - "num_extents": { - "type": "long" - }, - "objects": { - "type": "long" - }, - "storage_size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "metrics": { - "properties": { - "commands": { - "properties": { - "aggregate": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "build_info": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "coll_stats": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "connection_pool_stats": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "count": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "db_stats": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "distinct": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "find": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "get_cmd_line_opts": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "get_last_error": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "get_log": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "get_more": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "get_parameter": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "host_info": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "insert": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "is_master": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "is_self": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "last_collections": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "last_commands": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "list_databased": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "list_indexes": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "ping": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "profile": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "replset_get_rbid": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "replset_get_status": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "replset_heartbeat": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "replset_update_position": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "server_status": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "update": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "whatsmyuri": { - "properties": { - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "cursor": { - "properties": { - "open": { - "properties": { - "no_timeout": { - "type": "long" - }, - "pinned": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "timed_out": { - "type": "long" - } - } - }, - "document": { - "properties": { - "deleted": { - "type": "long" - }, - "inserted": { - "type": "long" - }, - "returned": { - "type": "long" - }, - "updated": { - "type": "long" - } - } - }, - "get_last_error": { - "properties": { - "write_timeouts": { - "type": "long" - }, - "write_wait": { - "properties": { - "count": { - "type": "long" - }, - "ms": { - "type": "long" - } - } - } - } - }, - "operation": { - "properties": { - "scan_and_order": { - "type": "long" - }, - "write_conflicts": { - "type": "long" - } - } - }, - "query_executor": { - "properties": { - "scanned_documents": { - "properties": { - "count": { - "type": "long" - } - } - }, - "scanned_indexes": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "replication": { - "properties": { - "apply": { - "properties": { - "attempts_to_become_secondary": { - "type": "long" - }, - "batches": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "ops": { - "type": "long" - } - } - }, - "buffer": { - "properties": { - "count": { - "type": "long" - }, - "max_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "executor": { - "properties": { - "counters": { - "properties": { - "cancels": { - "type": "long" - }, - "event_created": { - "type": "long" - }, - "event_wait": { - "type": "long" - }, - "scheduled": { - "properties": { - "dbwork": { - "type": "long" - }, - "exclusive": { - "type": "long" - }, - "failures": { - "type": "long" - }, - "netcmd": { - "type": "long" - }, - "work": { - "type": "long" - }, - "work_at": { - "type": "long" - } - } - }, - "waits": { - "type": "long" - } - } - }, - "event_waiters": { - "type": "long" - }, - "network_interface": { - "ignore_above": 1024, - "type": "keyword" - }, - "queues": { - "properties": { - "free": { - "type": "long" - }, - "in_progress": { - "properties": { - "dbwork": { - "type": "long" - }, - "exclusive": { - "type": "long" - }, - "network": { - "type": "long" - } - } - }, - "ready": { - "type": "long" - }, - "sleepers": { - "type": "long" - } - } - }, - "shutting_down": { - "type": "boolean" - }, - "unsignaled_events": { - "type": "long" - } - } - }, - "initial_sync": { - "properties": { - "completed": { - "type": "long" - }, - "failed_attempts": { - "type": "long" - }, - "failures": { - "type": "long" - } - } - }, - "network": { - "properties": { - "bytes": { - "type": "long" - }, - "getmores": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "ops": { - "type": "long" - }, - "reders_created": { - "type": "long" - } - } - }, - "preload": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "indexes": { - "properties": { - "count": { - "type": "long" - }, - "time": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "storage": { - "properties": { - "free_list": { - "properties": { - "search": { - "properties": { - "bucket_exhausted": { - "type": "long" - }, - "requests": { - "type": "long" - }, - "scanned": { - "type": "long" - } - } - } - } - } - } - }, - "ttl": { - "properties": { - "deleted_documents": { - "properties": { - "count": { - "type": "long" - } - } - }, - "passes": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "replstatus": { - "properties": { - "headroom": { - "properties": { - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "lag": { - "properties": { - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "members": { - "properties": { - "arbiter": { - "properties": { - "count": { - "type": "long" - }, - "hosts": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "down": { - "properties": { - "count": { - "type": "long" - }, - "hosts": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "primary": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "optime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "recovering": { - "properties": { - "count": { - "type": "long" - }, - "hosts": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rollback": { - "properties": { - "count": { - "type": "long" - }, - "hosts": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "secondary": { - "properties": { - "count": { - "type": "long" - }, - "hosts": { - "ignore_above": 1024, - "type": "keyword" - }, - "optimes": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "startup2": { - "properties": { - "count": { - "type": "long" - }, - "hosts": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "unhealthy": { - "properties": { - "count": { - "type": "long" - }, - "hosts": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "unknown": { - "properties": { - "count": { - "type": "long" - }, - "hosts": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "oplog": { - "properties": { - "first": { - "properties": { - "timestamp": { - "type": "long" - } - } - }, - "last": { - "properties": { - "timestamp": { - "type": "long" - } - } - }, - "size": { - "properties": { - "allocated": { - "type": "long" - }, - "used": { - "type": "long" - } - } - }, - "window": { - "type": "long" - } - } - }, - "optimes": { - "properties": { - "applied": { - "type": "long" - }, - "durable": { - "type": "long" - }, - "last_committed": { - "type": "long" - } - } - }, - "server_date": { - "type": "date" - }, - "set_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "status": { - "properties": { - "asserts": { - "properties": { - "msg": { - "type": "long" - }, - "regular": { - "type": "long" - }, - "rollovers": { - "type": "long" - }, - "user": { - "type": "long" - }, - "warning": { - "type": "long" - } - } - }, - "background_flushing": { - "properties": { - "average": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "flushes": { - "type": "long" - }, - "last": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "last_finished": { - "type": "date" - }, - "total": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "connections": { - "properties": { - "available": { - "type": "long" - }, - "current": { - "type": "long" - }, - "total_created": { - "type": "long" - } - } - }, - "extra_info": { - "properties": { - "heap_usage": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "page_faults": { - "type": "long" - } - } - }, - "global_lock": { - "properties": { - "active_clients": { - "properties": { - "readers": { - "type": "long" - }, - "total": { - "type": "long" - }, - "writers": { - "type": "long" - } - } - }, - "current_queue": { - "properties": { - "readers": { - "type": "long" - }, - "total": { - "type": "long" - }, - "writers": { - "type": "long" - } - } - }, - "total_time": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "journaling": { - "properties": { - "commits": { - "type": "long" - }, - "commits_in_write_lock": { - "type": "long" - }, - "compression": { - "type": "long" - }, - "early_commits": { - "type": "long" - }, - "journaled": { - "properties": { - "mb": { - "type": "long" - } - } - }, - "times": { - "properties": { - "commits": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "commits_in_write_lock": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "dt": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "prep_log_buffer": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "remap_private_view": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "write_to_data_files": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "write_to_journal": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "write_to_data_files": { - "properties": { - "mb": { - "type": "long" - } - } - } - } - }, - "local_time": { - "type": "date" - }, - "locks": { - "properties": { - "collection": { - "properties": { - "acquire": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "deadlock": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "wait": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - }, - "us": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - } - } - }, - "database": { - "properties": { - "acquire": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "deadlock": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "wait": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - }, - "us": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - } - } - }, - "global": { - "properties": { - "acquire": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "deadlock": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "wait": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - }, - "us": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - } - } - }, - "meta_data": { - "properties": { - "acquire": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "deadlock": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "wait": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - }, - "us": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - } - } - }, - "oplog": { - "properties": { - "acquire": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "deadlock": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - }, - "wait": { - "properties": { - "count": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - }, - "us": { - "properties": { - "R": { - "type": "long" - }, - "W": { - "type": "long" - }, - "r": { - "type": "long" - }, - "w": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "memory": { - "properties": { - "bits": { - "type": "long" - }, - "mapped": { - "properties": { - "mb": { - "type": "long" - } - } - }, - "mapped_with_journal": { - "properties": { - "mb": { - "type": "long" - } - } - }, - "resident": { - "properties": { - "mb": { - "type": "long" - } - } - }, - "virtual": { - "properties": { - "mb": { - "type": "long" - } - } - } - } - }, - "network": { - "properties": { - "in": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "out": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "requests": { - "type": "long" - } - } - }, - "ops": { - "properties": { - "counters": { - "properties": { - "command": { - "type": "long" - }, - "delete": { - "type": "long" - }, - "getmore": { - "type": "long" - }, - "insert": { - "type": "long" - }, - "query": { - "type": "long" - }, - "update": { - "type": "long" - } - } - }, - "latencies": { - "properties": { - "commands": { - "properties": { - "count": { - "type": "long" - }, - "latency": { - "type": "long" - } - } - }, - "reads": { - "properties": { - "count": { - "type": "long" - }, - "latency": { - "type": "long" - } - } - }, - "writes": { - "properties": { - "count": { - "type": "long" - }, - "latency": { - "type": "long" - } - } - } - } - }, - "replicated": { - "properties": { - "command": { - "type": "long" - }, - "delete": { - "type": "long" - }, - "getmore": { - "type": "long" - }, - "insert": { - "type": "long" - }, - "query": { - "type": "long" - }, - "update": { - "type": "long" - } - } - } - } - }, - "process": { - "path": "process.name", - "type": "alias" - }, - "storage_engine": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "version": { - "path": "service.version", - "type": "alias" - }, - "wired_tiger": { - "properties": { - "cache": { - "properties": { - "dirty": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "maximum": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "pages": { - "properties": { - "evicted": { - "type": "long" - }, - "read": { - "type": "long" - }, - "write": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "concurrent_transactions": { - "properties": { - "read": { - "properties": { - "available": { - "type": "long" - }, - "out": { - "type": "long" - }, - "total_tickets": { - "type": "long" - } - } - }, - "write": { - "properties": { - "available": { - "type": "long" - }, - "out": { - "type": "long" - }, - "total_tickets": { - "type": "long" - } - } - } - } - }, - "log": { - "properties": { - "flushes": { - "type": "long" - }, - "max_file_size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "scans": { - "type": "long" - }, - "size": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "syncs": { - "type": "long" - }, - "write": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "writes": { - "type": "long" - } - } - } - } - }, - "write_backs_queued": { - "type": "boolean" - } - } - } - } - }, - "munin": { - "properties": { - "metrics": { - "properties": { - "*": { - "type": "object" - } - } - }, - "plugin": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "mysql": { - "properties": { - "galera_status": { - "properties": { - "apply": { - "properties": { - "oooe": { - "type": "double" - }, - "oool": { - "type": "double" - }, - "window": { - "type": "double" - } - } - }, - "cert": { - "properties": { - "deps_distance": { - "type": "double" - }, - "index_size": { - "type": "long" - }, - "interval": { - "type": "double" - } - } - }, - "cluster": { - "properties": { - "conf_id": { - "type": "long" - }, - "size": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "commit": { - "properties": { - "oooe": { - "type": "double" - }, - "window": { - "type": "long" - } - } - }, - "connected": { - "ignore_above": 1024, - "type": "keyword" - }, - "evs": { - "properties": { - "evict": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "flow_ctl": { - "properties": { - "paused": { - "type": "double" - }, - "paused_ns": { - "type": "long" - }, - "recv": { - "type": "long" - }, - "sent": { - "type": "long" - } - } - }, - "last_committed": { - "type": "long" - }, - "local": { - "properties": { - "bf_aborts": { - "type": "long" - }, - "cert_failures": { - "type": "long" - }, - "commits": { - "type": "long" - }, - "recv": { - "properties": { - "queue": { - "type": "long" - }, - "queue_avg": { - "type": "double" - }, - "queue_max": { - "type": "long" - }, - "queue_min": { - "type": "long" - } - } - }, - "replays": { - "type": "long" - }, - "send": { - "properties": { - "queue": { - "type": "long" - }, - "queue_avg": { - "type": "double" - }, - "queue_max": { - "type": "long" - }, - "queue_min": { - "type": "long" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ready": { - "ignore_above": 1024, - "type": "keyword" - }, - "received": { - "properties": { - "bytes": { - "type": "long" - }, - "count": { - "type": "long" - } - } - }, - "repl": { - "properties": { - "bytes": { - "type": "long" - }, - "count": { - "type": "long" - }, - "data_bytes": { - "type": "long" - }, - "keys": { - "type": "long" - }, - "keys_bytes": { - "type": "long" - }, - "other_bytes": { - "type": "long" - } - } - } - } - }, - "performance": { - "properties": { - "events_statements": { - "properties": { - "avg": { - "properties": { - "timer": { - "properties": { - "wait": { - "type": "long" - } - } - } - } - }, - "count": { - "properties": { - "star": { - "type": "long" - } - } - }, - "digest": { - "norms": false, - "type": "text" - }, - "last": { - "properties": { - "seen": { - "type": "date" - } - } - }, - "max": { - "properties": { - "timer": { - "properties": { - "wait": { - "type": "long" - } - } - } - } - }, - "quantile": { - "properties": { - "95": { - "type": "long" - } - } - } - } - }, - "table_io_waits": { - "properties": { - "count": { - "properties": { - "fetch": { - "type": "long" - } - } - }, - "index": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "object": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "schema": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "status": { - "properties": { - "aborted": { - "properties": { - "clients": { - "type": "long" - }, - "connects": { - "type": "long" - } - } - }, - "binlog": { - "properties": { - "cache": { - "properties": { - "disk_use": { - "type": "long" - }, - "use": { - "type": "long" - } - } - } - } - }, - "bytes": { - "properties": { - "received": { - "type": "long" - }, - "sent": { - "type": "long" - } - } - }, - "cache": { - "properties": { - "ssl": { - "properties": { - "hits": { - "type": "long" - }, - "misses": { - "type": "long" - }, - "size": { - "type": "long" - } - } - }, - "table": { - "properties": { - "open_cache": { - "properties": { - "hits": { - "type": "long" - }, - "misses": { - "type": "long" - }, - "overflows": { - "type": "long" - } - } - } - } - } - } - }, - "command": { - "properties": { - "delete": { - "type": "long" - }, - "insert": { - "type": "long" - }, - "select": { - "type": "long" - }, - "update": { - "type": "long" - } - } - }, - "connection": { - "properties": { - "errors": { - "properties": { - "accept": { - "type": "long" - }, - "internal": { - "type": "long" - }, - "max": { - "type": "long" - }, - "peer_address": { - "type": "long" - }, - "select": { - "type": "long" - }, - "tcpwrap": { - "type": "long" - } - } - } - } - }, - "connections": { - "type": "long" - }, - "created": { - "properties": { - "tmp": { - "properties": { - "disk_tables": { - "type": "long" - }, - "files": { - "type": "long" - }, - "tables": { - "type": "long" - } - } - } - } - }, - "delayed": { - "properties": { - "errors": { - "type": "long" - }, - "insert_threads": { - "type": "long" - }, - "writes": { - "type": "long" - } - } - }, - "flush_commands": { - "type": "long" - }, - "handler": { - "properties": { - "commit": { - "type": "long" - }, - "delete": { - "type": "long" - }, - "external_lock": { - "type": "long" - }, - "mrr_init": { - "type": "long" - }, - "prepare": { - "type": "long" - }, - "read": { - "properties": { - "first": { - "type": "long" - }, - "key": { - "type": "long" - }, - "last": { - "type": "long" - }, - "next": { - "type": "long" - }, - "prev": { - "type": "long" - }, - "rnd": { - "type": "long" - }, - "rnd_next": { - "type": "long" - } - } - }, - "rollback": { - "type": "long" - }, - "savepoint": { - "type": "long" - }, - "savepoint_rollback": { - "type": "long" - }, - "update": { - "type": "long" - }, - "write": { - "type": "long" - } - } - }, - "innodb": { - "properties": { - "buffer_pool": { - "properties": { - "bytes": { - "properties": { - "data": { - "type": "long" - }, - "dirty": { - "type": "long" - } - } - }, - "dump_status": { - "type": "long" - }, - "load_status": { - "type": "long" - }, - "pages": { - "properties": { - "data": { - "type": "long" - }, - "dirty": { - "type": "long" - }, - "flushed": { - "type": "long" - }, - "free": { - "type": "long" - }, - "latched": { - "type": "long" - }, - "misc": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "pool": { - "properties": { - "reads": { - "type": "long" - }, - "resize_status": { - "type": "long" - }, - "wait_free": { - "type": "long" - } - } - }, - "read": { - "properties": { - "ahead": { - "type": "long" - }, - "ahead_evicted": { - "type": "long" - }, - "ahead_rnd": { - "type": "long" - }, - "requests": { - "type": "long" - } - } - }, - "write_requests": { - "type": "long" - } - } - }, - "rows": { - "properties": { - "deleted": { - "type": "long" - }, - "inserted": { - "type": "long" - }, - "reads": { - "type": "long" - }, - "updated": { - "type": "long" - } - } - } - } - }, - "max_used_connections": { - "type": "long" - }, - "open": { - "properties": { - "files": { - "type": "long" - }, - "streams": { - "type": "long" - }, - "tables": { - "type": "long" - } - } - }, - "opened_tables": { - "type": "long" - }, - "queries": { - "type": "long" - }, - "questions": { - "type": "long" - }, - "threads": { - "properties": { - "cached": { - "type": "long" - }, - "connected": { - "type": "long" - }, - "created": { - "type": "long" - }, - "running": { - "type": "long" - } - } - } - } - } - } - }, - "nats": { - "properties": { - "connection": { - "properties": { - "idle_time": { - "type": "long" - }, - "in": { - "properties": { - "bytes": { - "type": "long" - }, - "messages": { - "type": "long" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "out": { - "properties": { - "bytes": { - "type": "long" - }, - "messages": { - "type": "long" - } - } - }, - "pending_bytes": { - "type": "long" - }, - "subscriptions": { - "type": "long" - }, - "uptime": { - "type": "long" - } - } - }, - "connections": { - "properties": { - "total": { - "type": "long" - } - } - }, - "route": { - "properties": { - "in": { - "properties": { - "bytes": { - "type": "long" - }, - "messages": { - "type": "long" - } - } - }, - "ip": { - "type": "ip" - }, - "out": { - "properties": { - "bytes": { - "type": "long" - }, - "messages": { - "type": "long" - } - } - }, - "pending_size": { - "type": "long" - }, - "port": { - "type": "long" - }, - "remote_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "subscriptions": { - "type": "long" - } - } - }, - "routes": { - "properties": { - "total": { - "type": "long" - } - } - }, - "server": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "time": { - "type": "date" - } - } - }, - "stats": { - "properties": { - "cores": { - "type": "long" - }, - "cpu": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "http": { - "properties": { - "req_stats": { - "properties": { - "uri": { - "properties": { - "connz": { - "type": "long" - }, - "root": { - "type": "long" - }, - "routez": { - "type": "long" - }, - "subsz": { - "type": "long" - }, - "varz": { - "type": "long" - } - } - } - } - } - } - }, - "in": { - "properties": { - "bytes": { - "type": "long" - }, - "messages": { - "type": "long" - } - } - }, - "mem": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "out": { - "properties": { - "bytes": { - "type": "long" - }, - "messages": { - "type": "long" - } - } - }, - "remotes": { - "type": "long" - }, - "slow_consumers": { - "type": "long" - }, - "total_connections": { - "type": "long" - }, - "uptime": { - "type": "long" - } - } - }, - "subscriptions": { - "properties": { - "cache": { - "properties": { - "fanout": { - "properties": { - "avg": { - "type": "double" - }, - "max": { - "type": "long" - } - } - }, - "hit_rate": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "size": { - "type": "long" - } - } - }, - "inserts": { - "type": "long" - }, - "matches": { - "type": "long" - }, - "removes": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "nginx": { - "properties": { - "stubstatus": { - "properties": { - "accepts": { - "type": "long" - }, - "active": { - "type": "long" - }, - "current": { - "type": "long" - }, - "dropped": { - "type": "long" - }, - "handled": { - "type": "long" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "reading": { - "type": "long" - }, - "requests": { - "type": "long" - }, - "waiting": { - "type": "long" - }, - "writing": { - "type": "long" - } - } - } - } - }, - "node_stats": { - "properties": { - "fs": { - "properties": { - "io_stats": { - "properties": { - "total": { - "properties": { - "operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.operations.count", - "type": "alias" - }, - "read_operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.read.operations.count", - "type": "alias" - }, - "write_operations": { - "path": "elasticsearch.node.stats.fs.io_stats.total.write.operations.count", - "type": "alias" - } - } - } - } - }, - "summary": { - "properties": { - "available": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.fs.summary.available.bytes", - "type": "alias" - } - } - }, - "total": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.fs.summary.total.bytes", - "type": "alias" - } - } - } - } - }, - "total": { - "properties": { - "available_in_bytes": { - "path": "elasticsearch.node.stats.fs.summary.available.bytes", - "type": "alias" - }, - "total_in_bytes": { - "path": "elasticsearch.node.stats.fs.summary.total.bytes", - "type": "alias" - } - } - } - } - }, - "indices": { - "properties": { - "docs": { - "properties": { - "count": { - "path": "elasticsearch.node.stats.indices.docs.count", - "type": "alias" - } - } - }, - "fielddata": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.fielddata.memory.bytes", - "type": "alias" - } - } - }, - "indexing": { - "properties": { - "index_time_in_millis": { - "path": "elasticsearch.node.stats.indices.indexing.index_time.ms", - "type": "alias" - }, - "index_total": { - "path": "elasticsearch.node.stats.indices.indexing.index_total.count", - "type": "alias" - }, - "throttle_time_in_millis": { - "path": "elasticsearch.node.stats.indices.indexing.throttle_time.ms", - "type": "alias" - } - } - }, - "query_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.query_cache.memory.bytes", - "type": "alias" - } - } - }, - "request_cache": { - "properties": { - "memory_size_in_bytes": { - "path": "elasticsearch.node.stats.indices.request_cache.memory.bytes", - "type": "alias" - } - } - }, - "search": { - "properties": { - "query_time_in_millis": { - "path": "elasticsearch.node.stats.indices.search.query_time.ms", - "type": "alias" - }, - "query_total": { - "path": "elasticsearch.node.stats.indices.search.query_total.count", - "type": "alias" - } - } - }, - "segments": { - "properties": { - "count": { - "path": "elasticsearch.node.stats.indices.segments.count", - "type": "alias" - }, - "doc_values_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.doc_values.memory.bytes", - "type": "alias" - }, - "fixed_bit_set_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.fixed_bit_set.memory.bytes", - "type": "alias" - }, - "index_writer_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.index_writer.memory.bytes", - "type": "alias" - }, - "memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.memory.bytes", - "type": "alias" - }, - "norms_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.norms.memory.bytes", - "type": "alias" - }, - "points_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.points.memory.bytes", - "type": "alias" - }, - "stored_fields_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.stored_fields.memory.bytes", - "type": "alias" - }, - "term_vectors_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.term_vectors.memory.bytes", - "type": "alias" - }, - "terms_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.terms.memory.bytes", - "type": "alias" - }, - "version_map_memory_in_bytes": { - "path": "elasticsearch.node.stats.indices.segments.version_map.memory.bytes", - "type": "alias" - } - } - }, - "store": { - "properties": { - "size": { - "properties": { - "bytes": { - "path": "elasticsearch.node.stats.indices.store.size.bytes", - "type": "alias" - } - } - }, - "size_in_bytes": { - "path": "elasticsearch.node.stats.indices.store.size.bytes", - "type": "alias" - } - } - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "collectors": { - "properties": { - "old": { - "properties": { - "collection_count": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.count", - "type": "alias" - }, - "collection_time_in_millis": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms", - "type": "alias" - } - } - }, - "young": { - "properties": { - "collection_count": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.count", - "type": "alias" - }, - "collection_time_in_millis": { - "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms", - "type": "alias" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "heap_max_in_bytes": { - "path": "elasticsearch.node.stats.jvm.mem.heap.max.bytes", - "type": "alias" - }, - "heap_used_in_bytes": { - "path": "elasticsearch.node.stats.jvm.mem.heap.used.bytes", - "type": "alias" - }, - "heap_used_percent": { - "path": "elasticsearch.node.stats.jvm.mem.heap.used.pct", - "type": "alias" - } - } - } - } - }, - "node_id": { - "path": "elasticsearch.node.id", - "type": "alias" - }, - "os": { - "properties": { - "cgroup": { - "properties": { - "cpu": { - "properties": { - "cfs_quota_micros": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.cfs.quota.us", - "type": "alias" - }, - "stat": { - "properties": { - "number_of_elapsed_periods": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.elapsed_periods.count", - "type": "alias" - }, - "number_of_times_throttled": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.times_throttled.count", - "type": "alias" - }, - "time_throttled_nanos": { - "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.time_throttled.ns", - "type": "alias" - } - } - } - } - }, - "cpuacct": { - "properties": { - "usage_nanos": { - "path": "elasticsearch.node.stats.os.cgroup.cpuacct.usage.ns", - "type": "alias" - } - } - }, - "memory": { - "properties": { - "control_group": { - "path": "elasticsearch.node.stats.os.cgroup.memory.control_group", - "type": "alias" - }, - "limit_in_bytes": { - "path": "elasticsearch.node.stats.os.cgroup.memory.limit.bytes", - "type": "alias" - }, - "usage_in_bytes": { - "path": "elasticsearch.node.stats.os.cgroup.memory.usage.bytes", - "type": "alias" - } - } - } - } - }, - "cpu": { - "properties": { - "load_average": { - "properties": { - "1m": { - "path": "elasticsearch.node.stats.os.cpu.load_avg.1m", - "type": "alias" - } - } - } - } - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "percent": { - "path": "elasticsearch.node.stats.process.cpu.pct", - "type": "alias" - } - } - } - } - }, - "thread_pool": { - "properties": { - "bulk": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.bulk.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.bulk.rejected.count", - "type": "alias" - } - } - }, - "get": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.get.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.get.rejected.count", - "type": "alias" - } - } - }, - "index": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.index.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.index.rejected.count", - "type": "alias" - } - } - }, - "search": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.search.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.search.rejected.count", - "type": "alias" - } - } - }, - "write": { - "properties": { - "queue": { - "path": "elasticsearch.node.stats.thread_pool.write.queue.count", - "type": "alias" - }, - "rejected": { - "path": "elasticsearch.node.stats.thread_pool.write.rejected.count", - "type": "alias" - } - } - } - } - } - } - }, - "observer": { - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "php_fpm": { - "properties": { - "pool": { - "properties": { - "connections": { - "properties": { - "accepted": { - "type": "long" - }, - "listen_queue_len": { - "type": "long" - }, - "max_listen_queue": { - "type": "long" - }, - "queued": { - "type": "long" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "process_manager": { - "ignore_above": 1024, - "type": "keyword" - }, - "processes": { - "properties": { - "active": { - "type": "long" - }, - "idle": { - "type": "long" - }, - "max_active": { - "type": "long" - }, - "max_children_reached": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "slow_requests": { - "type": "long" - }, - "start_since": { - "type": "long" - }, - "start_time": { - "type": "date" - } - } - }, - "process": { - "properties": { - "last_request_cpu": { - "type": "long" - }, - "last_request_memory": { - "type": "long" - }, - "request_duration": { - "type": "long" - }, - "requests": { - "type": "long" - }, - "script": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_since": { - "type": "long" - }, - "start_time": { - "type": "date" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "postgresql": { - "properties": { - "activity": { - "properties": { - "application_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "backend_start": { - "type": "date" - }, - "backend_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - } - } - }, - "database": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "oid": { - "type": "long" - } - } - }, - "pid": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "query_start": { - "type": "date" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "state_change": { - "type": "date" - }, - "transaction_start": { - "type": "date" - }, - "user": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "wait_event": { - "ignore_above": 1024, - "type": "keyword" - }, - "wait_event_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "waiting": { - "type": "boolean" - } - } - }, - "bgwriter": { - "properties": { - "buffers": { - "properties": { - "allocated": { - "type": "long" - }, - "backend": { - "type": "long" - }, - "backend_fsync": { - "type": "long" - }, - "checkpoints": { - "type": "long" - }, - "clean": { - "type": "long" - }, - "clean_full": { - "type": "long" - } - } - }, - "checkpoints": { - "properties": { - "requested": { - "type": "long" - }, - "scheduled": { - "type": "long" - }, - "times": { - "properties": { - "sync": { - "properties": { - "ms": { - "type": "float" - } - } - }, - "write": { - "properties": { - "ms": { - "type": "float" - } - } - } - } - } - } - }, - "stats_reset": { - "type": "date" - } - } - }, - "database": { - "properties": { - "blocks": { - "properties": { - "hit": { - "type": "long" - }, - "read": { - "type": "long" - }, - "time": { - "properties": { - "read": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "write": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "conflicts": { - "type": "long" - }, - "deadlocks": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "number_of_backends": { - "type": "long" - }, - "oid": { - "type": "long" - }, - "rows": { - "properties": { - "deleted": { - "type": "long" - }, - "fetched": { - "type": "long" - }, - "inserted": { - "type": "long" - }, - "returned": { - "type": "long" - }, - "updated": { - "type": "long" - } - } - }, - "stats_reset": { - "type": "date" - }, - "temporary": { - "properties": { - "bytes": { - "type": "long" - }, - "files": { - "type": "long" - } - } - }, - "transactions": { - "properties": { - "commit": { - "type": "long" - }, - "rollback": { - "type": "long" - } - } - } - } - }, - "statement": { - "properties": { - "database": { - "properties": { - "oid": { - "type": "long" - } - } - }, - "query": { - "properties": { - "calls": { - "type": "long" - }, - "id": { - "type": "long" - }, - "memory": { - "properties": { - "local": { - "properties": { - "dirtied": { - "type": "long" - }, - "hit": { - "type": "long" - }, - "read": { - "type": "long" - }, - "written": { - "type": "long" - } - } - }, - "shared": { - "properties": { - "dirtied": { - "type": "long" - }, - "hit": { - "type": "long" - }, - "read": { - "type": "long" - }, - "written": { - "type": "long" - } - } - }, - "temp": { - "properties": { - "read": { - "type": "long" - }, - "written": { - "type": "long" - } - } - } - } - }, - "rows": { - "type": "long" - }, - "text": { - "ignore_above": 1024, - "type": "keyword" - }, - "time": { - "properties": { - "max": { - "properties": { - "ms": { - "type": "float" - } - } - }, - "mean": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "min": { - "properties": { - "ms": { - "type": "float" - } - } - }, - "stddev": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "total": { - "properties": { - "ms": { - "type": "float" - } - } - } - } - } - } - }, - "user": { - "properties": { - "id": { - "type": "long" - } - } - } - } - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "cpu": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "start_time": { - "type": "date" - } - } - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "memory": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "prometheus": { - "properties": { - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "metrics": { - "properties": { - "*": { - "type": "object" - } - } - }, - "query": { - "properties": { - "*": { - "type": "object" - } - } - } - } - }, - "rabbitmq": { - "properties": { - "connection": { - "properties": { - "channel_max": { - "type": "long" - }, - "channels": { - "type": "long" - }, - "client_provided": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "frame_max": { - "type": "long" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "octet_count": { - "properties": { - "received": { - "type": "long" - }, - "sent": { - "type": "long" - } - } - }, - "packet_count": { - "properties": { - "pending": { - "type": "long" - }, - "received": { - "type": "long" - }, - "sent": { - "type": "long" - } - } - }, - "peer": { - "properties": { - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - } - } - }, - "port": { - "type": "long" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "exchange": { - "properties": { - "auto_delete": { - "type": "boolean" - }, - "durable": { - "type": "boolean" - }, - "internal": { - "type": "boolean" - }, - "messages": { - "properties": { - "publish_in": { - "properties": { - "count": { - "type": "long" - }, - "details": { - "properties": { - "rate": { - "type": "float" - } - } - } - } - }, - "publish_out": { - "properties": { - "count": { - "type": "long" - }, - "details": { - "properties": { - "rate": { - "type": "float" - } - } - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "node": { - "properties": { - "disk": { - "properties": { - "free": { - "properties": { - "bytes": { - "type": "long" - }, - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "fd": { - "properties": { - "total": { - "type": "long" - }, - "used": { - "type": "long" - } - } - }, - "gc": { - "properties": { - "num": { - "properties": { - "count": { - "type": "long" - } - } - }, - "reclaimed": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "io": { - "properties": { - "file_handle": { - "properties": { - "open_attempt": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "count": { - "type": "long" - } - } - } - } - }, - "read": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "bytes": { - "type": "long" - }, - "count": { - "type": "long" - } - } - }, - "reopen": { - "properties": { - "count": { - "type": "long" - } - } - }, - "seek": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "count": { - "type": "long" - } - } - }, - "sync": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "count": { - "type": "long" - } - } - }, - "write": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "bytes": { - "type": "long" - }, - "count": { - "type": "long" - } - } - } - } - }, - "mem": { - "properties": { - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "mnesia": { - "properties": { - "disk": { - "properties": { - "tx": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "ram": { - "properties": { - "tx": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "msg": { - "properties": { - "store_read": { - "properties": { - "count": { - "type": "long" - } - } - }, - "store_write": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "proc": { - "properties": { - "total": { - "type": "long" - }, - "used": { - "type": "long" - } - } - }, - "processors": { - "type": "long" - }, - "queue": { - "properties": { - "index": { - "properties": { - "journal_write": { - "properties": { - "count": { - "type": "long" - } - } - }, - "read": { - "properties": { - "count": { - "type": "long" - } - } - }, - "write": { - "properties": { - "count": { - "type": "long" - } - } - } - } - } - } - }, - "run": { - "properties": { - "queue": { - "type": "long" - } - } - }, - "socket": { - "properties": { - "total": { - "type": "long" - }, - "used": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "arguments": { - "properties": { - "max_priority": { - "type": "long" - } - } - }, - "auto_delete": { - "type": "boolean" - }, - "consumers": { - "properties": { - "count": { - "type": "long" - }, - "utilisation": { - "properties": { - "pct": { - "type": "long" - } - } - } - } - }, - "disk": { - "properties": { - "reads": { - "properties": { - "count": { - "type": "long" - } - } - }, - "writes": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "durable": { - "type": "boolean" - }, - "exclusive": { - "type": "boolean" - }, - "memory": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "messages": { - "properties": { - "persistent": { - "properties": { - "count": { - "type": "long" - } - } - }, - "ready": { - "properties": { - "count": { - "type": "long" - }, - "details": { - "properties": { - "rate": { - "type": "float" - } - } - } - } - }, - "total": { - "properties": { - "count": { - "type": "long" - }, - "details": { - "properties": { - "rate": { - "type": "float" - } - } - } - } - }, - "unacknowledged": { - "properties": { - "count": { - "type": "long" - }, - "details": { - "properties": { - "rate": { - "type": "float" - } - } - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vhost": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "redis": { - "properties": { - "info": { - "properties": { - "clients": { - "properties": { - "biggest_input_buf": { - "type": "long" - }, - "blocked": { - "type": "long" - }, - "connected": { - "type": "long" - }, - "longest_output_list": { - "type": "long" - }, - "max_input_buffer": { - "type": "long" - }, - "max_output_buffer": { - "type": "long" - } - } - }, - "cluster": { - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "cpu": { - "properties": { - "used": { - "properties": { - "sys": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "sys_children": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "user": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "user_children": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "memory": { - "properties": { - "active_defrag": { - "properties": { - "is_running": { - "type": "boolean" - } - } - }, - "allocator": { - "ignore_above": 1024, - "type": "keyword" - }, - "allocator_stats": { - "properties": { - "active": { - "type": "long" - }, - "allocated": { - "type": "long" - }, - "fragmentation": { - "properties": { - "bytes": { - "type": "long" - }, - "ratio": { - "type": "float" - } - } - }, - "resident": { - "type": "long" - }, - "rss": { - "properties": { - "bytes": { - "type": "long" - }, - "ratio": { - "type": "float" - } - } - } - } - }, - "fragmentation": { - "properties": { - "bytes": { - "type": "long" - }, - "ratio": { - "type": "float" - } - } - }, - "max": { - "properties": { - "policy": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "type": "long" - } - } - }, - "used": { - "properties": { - "dataset": { - "type": "long" - }, - "lua": { - "type": "long" - }, - "peak": { - "type": "long" - }, - "rss": { - "type": "long" - }, - "value": { - "type": "long" - } - } - } - } - }, - "persistence": { - "properties": { - "aof": { - "properties": { - "bgrewrite": { - "properties": { - "last_status": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "buffer": { - "properties": { - "size": { - "type": "long" - } - } - }, - "copy_on_write": { - "properties": { - "last_size": { - "type": "long" - } - } - }, - "enabled": { - "type": "boolean" - }, - "fsync": { - "properties": { - "delayed": { - "type": "long" - }, - "pending": { - "type": "long" - } - } - }, - "rewrite": { - "properties": { - "buffer": { - "properties": { - "size": { - "type": "long" - } - } - }, - "current_time": { - "properties": { - "sec": { - "type": "long" - } - } - }, - "in_progress": { - "type": "boolean" - }, - "last_time": { - "properties": { - "sec": { - "type": "long" - } - } - }, - "scheduled": { - "type": "boolean" - } - } - }, - "size": { - "properties": { - "base": { - "type": "long" - }, - "current": { - "type": "long" - } - } - }, - "write": { - "properties": { - "last_status": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "loading": { - "type": "boolean" - }, - "rdb": { - "properties": { - "bgsave": { - "properties": { - "current_time": { - "properties": { - "sec": { - "type": "long" - } - } - }, - "in_progress": { - "type": "boolean" - }, - "last_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "last_time": { - "properties": { - "sec": { - "type": "long" - } - } - } - } - }, - "copy_on_write": { - "properties": { - "last_size": { - "type": "long" - } - } - }, - "last_save": { - "properties": { - "changes_since": { - "type": "long" - }, - "time": { - "type": "long" - } - } - } - } - } - } - }, - "replication": { - "properties": { - "backlog": { - "properties": { - "active": { - "type": "long" - }, - "first_byte_offset": { - "type": "long" - }, - "histlen": { - "type": "long" - }, - "size": { - "type": "long" - } - } - }, - "connected_slaves": { - "type": "long" - }, - "master": { - "properties": { - "last_io_seconds_ago": { - "type": "long" - }, - "link_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "offset": { - "type": "long" - }, - "second_offset": { - "type": "long" - }, - "sync": { - "properties": { - "in_progress": { - "type": "boolean" - }, - "last_io_seconds_ago": { - "type": "long" - }, - "left_bytes": { - "type": "long" - } - } - } - } - }, - "master_offset": { - "type": "long" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "slave": { - "properties": { - "is_readonly": { - "type": "boolean" - }, - "offset": { - "type": "long" - }, - "priority": { - "type": "long" - } - } - } - } - }, - "server": { - "properties": { - "arch_bits": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "config_file": { - "ignore_above": 1024, - "type": "keyword" - }, - "gcc_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "git_dirty": { - "ignore_above": 1024, - "type": "keyword" - }, - "git_sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "hz": { - "type": "long" - }, - "lru_clock": { - "type": "long" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "multiplexing_api": { - "ignore_above": 1024, - "type": "keyword" - }, - "run_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "tcp_port": { - "type": "long" - }, - "uptime": { - "type": "long" - } - } - }, - "slowlog": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stats": { - "properties": { - "active_defrag": { - "properties": { - "hits": { - "type": "long" - }, - "key_hits": { - "type": "long" - }, - "key_misses": { - "type": "long" - }, - "misses": { - "type": "long" - } - } - }, - "commands_processed": { - "type": "long" - }, - "connections": { - "properties": { - "received": { - "type": "long" - }, - "rejected": { - "type": "long" - } - } - }, - "instantaneous": { - "properties": { - "input_kbps": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ops_per_sec": { - "type": "long" - }, - "output_kbps": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "keys": { - "properties": { - "evicted": { - "type": "long" - }, - "expired": { - "type": "long" - } - } - }, - "keyspace": { - "properties": { - "hits": { - "type": "long" - }, - "misses": { - "type": "long" - } - } - }, - "latest_fork_usec": { - "type": "long" - }, - "migrate_cached_sockets": { - "type": "long" - }, - "net": { - "properties": { - "input": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "output": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "pubsub": { - "properties": { - "channels": { - "type": "long" - }, - "patterns": { - "type": "long" - } - } - }, - "slave_expires_tracked_keys": { - "type": "long" - }, - "sync": { - "properties": { - "full": { - "type": "long" - }, - "partial": { - "properties": { - "err": { - "type": "long" - }, - "ok": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "key": { - "properties": { - "expire": { - "properties": { - "ttl": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "length": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "keyspace": { - "properties": { - "avg_ttl": { - "type": "long" - }, - "expires": { - "type": "long" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "keys": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shard": { - "properties": { - "index": { - "path": "elasticsearch.index.name", - "type": "alias" - }, - "node": { - "path": "elasticsearch.node.id", - "type": "alias" - }, - "primary": { - "path": "elasticsearch.shard.primary", - "type": "alias" - }, - "shard": { - "path": "elasticsearch.shard.number", - "type": "alias" - }, - "state": { - "path": "elasticsearch.shard.state", - "type": "alias" - } - } - }, - "source": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "source_node": { - "properties": { - "name": { - "path": "elasticsearch.node.name", - "type": "alias" - }, - "uuid": { - "path": "elasticsearch.node.id", - "type": "alias" - } - } - }, - "stack_stats": { - "properties": { - "apm": { - "properties": { - "found": { - "path": "elasticsearch.cluster.stats.stack.apm.found", - "type": "alias" - } - } - }, - "xpack": { - "properties": { - "ccr": { - "properties": { - "available": { - "path": "elasticsearch.cluster.stats.stack.xpack.ccr.available", - "type": "alias" - }, - "enabled": { - "path": "elasticsearch.cluster.stats.stack.xpack.ccr.enabled", - "type": "alias" - } - } - } - } - } - } - }, - "system": { - "properties": { - "core": { - "properties": { - "id": { - "type": "long" - }, - "idle": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "iowait": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "irq": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "nice": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "softirq": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "steal": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "system": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "user": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - } - } - }, - "cpu": { - "properties": { - "cores": { - "type": "long" - }, - "idle": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "iowait": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "irq": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "nice": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "softirq": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "steal": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "system": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - }, - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "user": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - } - } - } - } - }, - "diskio": { - "properties": { - "io": { - "properties": { - "ops": { - "type": "long" - }, - "time": { - "type": "long" - } - } - }, - "iostat": { - "properties": { - "await": { - "type": "float" - }, - "busy": { - "type": "float" - }, - "queue": { - "properties": { - "avg_size": { - "type": "float" - } - } - }, - "read": { - "properties": { - "await": { - "type": "float" - }, - "per_sec": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "request": { - "properties": { - "merges_per_sec": { - "type": "float" - }, - "per_sec": { - "type": "float" - } - } - } - } - }, - "request": { - "properties": { - "avg_size": { - "type": "float" - } - } - }, - "service_time": { - "type": "float" - }, - "write": { - "properties": { - "await": { - "type": "float" - }, - "per_sec": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "request": { - "properties": { - "merges_per_sec": { - "type": "float" - }, - "per_sec": { - "type": "float" - } - } - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "read": { - "properties": { - "bytes": { - "type": "long" - }, - "count": { - "type": "long" - }, - "time": { - "type": "long" - } - } - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "write": { - "properties": { - "bytes": { - "type": "long" - }, - "count": { - "type": "long" - }, - "time": { - "type": "long" - } - } - } - } - }, - "entropy": { - "properties": { - "available_bits": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "filesystem": { - "properties": { - "available": { - "type": "long" - }, - "device_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "files": { - "type": "long" - }, - "free": { - "type": "long" - }, - "free_files": { - "type": "long" - }, - "mount_point": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "fsstat": { - "properties": { - "count": { - "type": "long" - }, - "total_files": { - "type": "long" - }, - "total_size": { - "properties": { - "free": { - "type": "long" - }, - "total": { - "type": "long" - }, - "used": { - "type": "long" - } - } - } - } - }, - "load": { - "properties": { - "1": { - "scaling_factor": 100, - "type": "scaled_float" - }, - "15": { - "scaling_factor": 100, - "type": "scaled_float" - }, - "5": { - "scaling_factor": 100, - "type": "scaled_float" - }, - "cores": { - "type": "long" - }, - "norm": { - "properties": { - "1": { - "scaling_factor": 100, - "type": "scaled_float" - }, - "15": { - "scaling_factor": 100, - "type": "scaled_float" - }, - "5": { - "scaling_factor": 100, - "type": "scaled_float" - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "free": { - "type": "long" - }, - "hugepages": { - "properties": { - "default_size": { - "type": "long" - }, - "free": { - "type": "long" - }, - "reserved": { - "type": "long" - }, - "surplus": { - "type": "long" - }, - "swap": { - "properties": { - "out": { - "properties": { - "fallback": { - "type": "long" - }, - "pages": { - "type": "long" - } - } - } - } - }, - "total": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "long" - } - } - } - } - }, - "page_stats": { - "properties": { - "direct_efficiency": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "kswapd_efficiency": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pgfree": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "pgscan_direct": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "pgscan_kswapd": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "pgsteal_direct": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "pgsteal_kswapd": { - "properties": { - "pages": { - "type": "long" - } - } - } - } - }, - "swap": { - "properties": { - "free": { - "type": "long" - }, - "in": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "out": { - "properties": { - "pages": { - "type": "long" - } - } - }, - "readahead": { - "properties": { - "cached": { - "type": "long" - }, - "pages": { - "type": "long" - } - } - }, - "total": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "total": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "network": { - "properties": { - "in": { - "properties": { - "bytes": { - "type": "long" - }, - "dropped": { - "type": "long" - }, - "errors": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "out": { - "properties": { - "bytes": { - "type": "long" - }, - "dropped": { - "type": "long" - }, - "errors": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - } - } - }, - "network_summary": { - "properties": { - "icmp": { - "properties": { - "*": { - "type": "object" - } - } - }, - "ip": { - "properties": { - "*": { - "type": "object" - } - } - }, - "tcp": { - "properties": { - "*": { - "type": "object" - } - } - }, - "udp": { - "properties": { - "*": { - "type": "object" - } - } - }, - "udp_lite": { - "properties": { - "*": { - "type": "object" - } - } - } - } - }, - "process": { - "properties": { - "cgroup": { - "properties": { - "blkio": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "properties": { - "bytes": { - "type": "long" - }, - "ios": { - "type": "long" - } - } - } - } - }, - "cpu": { - "properties": { - "cfs": { - "properties": { - "period": { - "properties": { - "us": { - "type": "long" - } - } - }, - "quota": { - "properties": { - "us": { - "type": "long" - } - } - }, - "shares": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rt": { - "properties": { - "period": { - "properties": { - "us": { - "type": "long" - } - } - }, - "runtime": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "stats": { - "properties": { - "periods": { - "type": "long" - }, - "throttled": { - "properties": { - "ns": { - "type": "long" - }, - "periods": { - "type": "long" - } - } - } - } - } - } - }, - "cpuacct": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "percpu": { - "type": "object" - }, - "stats": { - "properties": { - "system": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "user": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "kmem": { - "properties": { - "failures": { - "type": "long" - }, - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "long" - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "kmem_tcp": { - "properties": { - "failures": { - "type": "long" - }, - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "long" - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "mem": { - "properties": { - "failures": { - "type": "long" - }, - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "long" - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "memsw": { - "properties": { - "failures": { - "type": "long" - }, - "limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "long" - }, - "max": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "stats": { - "properties": { - "active_anon": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "active_file": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cache": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "hierarchical_memory_limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "hierarchical_memsw_limit": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "inactive_anon": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "inactive_file": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "major_page_faults": { - "type": "long" - }, - "mapped_file": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "page_faults": { - "type": "long" - }, - "pages_in": { - "type": "long" - }, - "pages_out": { - "type": "long" - }, - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "rss_huge": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "swap": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "unevictable": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cmdline": { - "ignore_above": 2048, - "type": "keyword" - }, - "cpu": { - "properties": { - "start_time": { - "type": "date" - }, - "system": { - "properties": { - "ticks": { - "type": "long" - } - } - }, - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - }, - "ticks": { - "type": "long" - }, - "value": { - "type": "long" - } - } - }, - "user": { - "properties": { - "ticks": { - "type": "long" - } - } - } - } - }, - "env": { - "type": "object" - }, - "fd": { - "properties": { - "limit": { - "properties": { - "hard": { - "type": "long" - }, - "soft": { - "type": "long" - } - } - }, - "open": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - }, - "share": { - "type": "long" - }, - "size": { - "type": "long" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "summary": { - "properties": { - "dead": { - "type": "long" - }, - "idle": { - "type": "long" - }, - "running": { - "type": "long" - }, - "sleeping": { - "type": "long" - }, - "stopped": { - "type": "long" - }, - "total": { - "type": "long" - }, - "unknown": { - "type": "long" - }, - "zombie": { - "type": "long" - } - } - } - } - }, - "raid": { - "properties": { - "blocks": { - "properties": { - "synced": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "disks": { - "properties": { - "active": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "spare": { - "type": "long" - }, - "states": { - "properties": { - "*": { - "type": "object" - } - } - }, - "total": { - "type": "long" - } - } - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync_action": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "exec_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "load_state": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resources": { - "properties": { - "cpu": { - "properties": { - "usage": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "usage": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "network": { - "properties": { - "in": { - "properties": { - "bytes": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - }, - "out": { - "properties": { - "bytes": { - "type": "long" - }, - "packets": { - "type": "long" - } - } - } - } - }, - "tasks": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "state_since": { - "type": "date" - }, - "sub_state": { - "ignore_above": 1024, - "type": "keyword" - }, - "unit_file": { - "properties": { - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor_preset": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "socket": { - "properties": { - "local": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cmdline": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "remote": { - "properties": { - "etld_plus_one": { - "ignore_above": 1024, - "type": "keyword" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "host_error": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "summary": { - "properties": { - "all": { - "properties": { - "count": { - "type": "long" - }, - "listening": { - "type": "long" - } - } - }, - "tcp": { - "properties": { - "all": { - "properties": { - "close_wait": { - "type": "long" - }, - "closing": { - "type": "long" - }, - "count": { - "type": "long" - }, - "established": { - "type": "long" - }, - "fin_wait1": { - "type": "long" - }, - "fin_wait2": { - "type": "long" - }, - "last_ack": { - "type": "long" - }, - "listening": { - "type": "long" - }, - "orphan": { - "type": "long" - }, - "syn_recv": { - "type": "long" - }, - "syn_sent": { - "type": "long" - }, - "time_wait": { - "type": "long" - } - } - }, - "memory": { - "type": "long" - } - } - }, - "udp": { - "properties": { - "all": { - "properties": { - "count": { - "type": "long" - } - } - }, - "memory": { - "type": "long" - } - } - } - } - } - } - }, - "uptime": { - "properties": { - "duration": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "users": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "leader": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "remote": { - "type": "boolean" - }, - "remote_host": { - "ignore_above": 1024, - "type": "keyword" - }, - "scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "seat": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "systemd": { - "properties": { - "fragment_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "unit": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "path": "@timestamp", - "type": "alias" - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "traefik": { - "properties": { - "health": { - "properties": { - "response": { - "properties": { - "avg_time": { - "properties": { - "us": { - "type": "long" - } - } - }, - "count": { - "type": "long" - }, - "status_codes": { - "properties": { - "*": { - "type": "object" - } - } - } - } - }, - "uptime": { - "properties": { - "sec": { - "type": "long" - } - } - } - } - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "uwsgi": { - "properties": { - "status": { - "properties": { - "core": { - "properties": { - "id": { - "type": "long" - }, - "read_errors": { - "type": "long" - }, - "requests": { - "properties": { - "offloaded": { - "type": "long" - }, - "routed": { - "type": "long" - }, - "static": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "worker_pid": { - "type": "long" - }, - "write_errors": { - "type": "long" - } - } - }, - "total": { - "properties": { - "exceptions": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "read_errors": { - "type": "long" - }, - "requests": { - "type": "long" - }, - "write_errors": { - "type": "long" - } - } - }, - "worker": { - "properties": { - "accepting": { - "type": "long" - }, - "avg_rt": { - "type": "long" - }, - "delta_requests": { - "type": "long" - }, - "exceptions": { - "type": "long" - }, - "harakiri_count": { - "type": "long" - }, - "id": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "requests": { - "type": "long" - }, - "respawn_count": { - "type": "long" - }, - "rss": { - "type": "long" - }, - "running_time": { - "type": "long" - }, - "signal_queue": { - "type": "long" - }, - "signals": { - "type": "long" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "tx": { - "type": "long" - }, - "vsz": { - "type": "long" - } - } - } - } - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vsphere": { - "properties": { - "datastore": { - "properties": { - "capacity": { - "properties": { - "free": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "fstype": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "cpu": { - "properties": { - "free": { - "properties": { - "mhz": { - "type": "long" - } - } - }, - "total": { - "properties": { - "mhz": { - "type": "long" - } - } - }, - "used": { - "properties": { - "mhz": { - "type": "long" - } - } - } - } - }, - "memory": { - "properties": { - "free": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "total": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "network_names": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "virtualmachine": { - "properties": { - "cpu": { - "properties": { - "used": { - "properties": { - "mhz": { - "type": "long" - } - } - } - } - }, - "custom_fields": { - "type": "object" - }, - "host": { - "properties": { - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "memory": { - "properties": { - "free": { - "properties": { - "guest": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "total": { - "properties": { - "guest": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - }, - "used": { - "properties": { - "guest": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "host": { - "properties": { - "bytes": { - "type": "long" - } - } - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "network_names": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "windows": { - "properties": { - "perfmon": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - }, - "metrics": { - "properties": { - "*": { - "properties": { - "*": { - "type": "object" - } - } - } - } - } - } - }, - "service": { - "properties": { - "display_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "pid": { - "type": "long" - }, - "start_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "zookeeper": { - "properties": { - "connection": { - "properties": { - "interest_ops": { - "type": "long" - }, - "queued": { - "type": "long" - }, - "received": { - "type": "long" - }, - "sent": { - "type": "long" - } - } - }, - "mntr": { - "properties": { - "approximate_data_size": { - "type": "long" - }, - "ephemerals_count": { - "type": "long" - }, - "followers": { - "type": "long" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "latency": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "max_file_descriptor_count": { - "type": "long" - }, - "num_alive_connections": { - "type": "long" - }, - "open_file_descriptor_count": { - "type": "long" - }, - "outstanding_requests": { - "type": "long" - }, - "packets": { - "properties": { - "received": { - "type": "long" - }, - "sent": { - "type": "long" - } - } - }, - "pending_syncs": { - "type": "long" - }, - "server_state": { - "ignore_above": 1024, - "type": "keyword" - }, - "synced_followers": { - "type": "long" - }, - "version": { - "path": "service.version", - "type": "alias" - }, - "watch_count": { - "type": "long" - }, - "znode_count": { - "type": "long" - } - } - }, - "server": { - "properties": { - "connections": { - "type": "long" - }, - "count": { - "type": "long" - }, - "epoch": { - "type": "long" - }, - "latency": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "node_count": { - "type": "long" - }, - "outstanding": { - "type": "long" - }, - "received": { - "type": "long" - }, - "sent": { - "type": "long" - }, - "version_date": { - "type": "date" - }, - "zxid": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "metricbeat", - "rollover_alias": "metricbeat-8.0.0" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "max_docvalue_fields_search": "200", - "number_of_replicas": "1", - "number_of_shards": "1", - "query": { - "default_field": [ - "message", - "tags", - "agent.ephemeral_id", - "agent.id", - "agent.name", - "agent.type", - "agent.version", - "as.organization.name", - "client.address", - "client.as.organization.name", - "client.domain", - "client.geo.city_name", - "client.geo.continent_name", - "client.geo.country_iso_code", - "client.geo.country_name", - "client.geo.name", - "client.geo.region_iso_code", - "client.geo.region_name", - "client.mac", - "client.registered_domain", - "client.top_level_domain", - "client.user.domain", - "client.user.email", - "client.user.full_name", - "client.user.group.domain", - "client.user.group.id", - "client.user.group.name", - "client.user.hash", - "client.user.id", - "client.user.name", - "cloud.account.id", - "cloud.availability_zone", - "cloud.instance.id", - "cloud.instance.name", - "cloud.machine.type", - "cloud.provider", - "cloud.region", - "container.id", - "container.image.name", - "container.image.tag", - "container.name", - "container.runtime", - "destination.address", - "destination.as.organization.name", - "destination.domain", - "destination.geo.city_name", - "destination.geo.continent_name", - "destination.geo.country_iso_code", - "destination.geo.country_name", - "destination.geo.name", - "destination.geo.region_iso_code", - "destination.geo.region_name", - "destination.mac", - "destination.registered_domain", - "destination.top_level_domain", - "destination.user.domain", - "destination.user.email", - "destination.user.full_name", - "destination.user.group.domain", - "destination.user.group.id", - "destination.user.group.name", - "destination.user.hash", - "destination.user.id", - "destination.user.name", - "dns.answers.class", - "dns.answers.data", - "dns.answers.name", - "dns.answers.type", - "dns.header_flags", - "dns.id", - "dns.op_code", - "dns.question.class", - "dns.question.name", - "dns.question.registered_domain", - "dns.question.subdomain", - "dns.question.top_level_domain", - "dns.question.type", - "dns.response_code", - "dns.type", - "ecs.version", - "error.code", - "error.id", - "error.message", - "error.stack_trace", - "error.type", - "event.action", - "event.category", - "event.code", - "event.dataset", - "event.hash", - "event.id", - "event.kind", - "event.module", - "event.original", - "event.outcome", - "event.provider", - "event.timezone", - "event.type", - "file.device", - "file.directory", - "file.extension", - "file.gid", - "file.group", - "file.hash.md5", - "file.hash.sha1", - "file.hash.sha256", - "file.hash.sha512", - "file.inode", - "file.mode", - "file.name", - "file.owner", - "file.path", - "file.target_path", - "file.type", - "file.uid", - "geo.city_name", - "geo.continent_name", - "geo.country_iso_code", - "geo.country_name", - "geo.name", - "geo.region_iso_code", - "geo.region_name", - "group.domain", - "group.id", - "group.name", - "hash.md5", - "hash.sha1", - "hash.sha256", - "hash.sha512", - "host.architecture", - "host.geo.city_name", - "host.geo.continent_name", - "host.geo.country_iso_code", - "host.geo.country_name", - "host.geo.name", - "host.geo.region_iso_code", - "host.geo.region_name", - "host.hostname", - "host.id", - "host.mac", - "host.name", - "host.os.family", - "host.os.full", - "host.os.kernel", - "host.os.name", - "host.os.platform", - "host.os.version", - "host.type", - "host.user.domain", - "host.user.email", - "host.user.full_name", - "host.user.group.domain", - "host.user.group.id", - "host.user.group.name", - "host.user.hash", - "host.user.id", - "host.user.name", - "http.request.body.content", - "http.request.method", - "http.request.referrer", - "http.response.body.content", - "http.version", - "log.level", - "log.logger", - "log.origin.file.name", - "log.origin.function", - "log.original", - "log.syslog.facility.name", - "log.syslog.severity.name", - "network.application", - "network.community_id", - "network.direction", - "network.iana_number", - "network.name", - "network.protocol", - "network.transport", - "network.type", - "observer.geo.city_name", - "observer.geo.continent_name", - "observer.geo.country_iso_code", - "observer.geo.country_name", - "observer.geo.name", - "observer.geo.region_iso_code", - "observer.geo.region_name", - "observer.hostname", - "observer.mac", - "observer.name", - "observer.os.family", - "observer.os.full", - "observer.os.kernel", - "observer.os.name", - "observer.os.platform", - "observer.os.version", - "observer.product", - "observer.serial_number", - "observer.type", - "observer.vendor", - "observer.version", - "organization.id", - "organization.name", - "os.family", - "os.full", - "os.kernel", - "os.name", - "os.platform", - "os.version", - "package.architecture", - "package.checksum", - "package.description", - "package.install_scope", - "package.license", - "package.name", - "package.path", - "package.version", - "process.args", - "text", - "process.executable", - "process.hash.md5", - "process.hash.sha1", - "process.hash.sha256", - "process.hash.sha512", - "process.name", - "text", - "text", - "text", - "text", - "text", - "process.thread.name", - "process.title", - "process.working_directory", - "server.address", - "server.as.organization.name", - "server.domain", - "server.geo.city_name", - "server.geo.continent_name", - "server.geo.country_iso_code", - "server.geo.country_name", - "server.geo.name", - "server.geo.region_iso_code", - "server.geo.region_name", - "server.mac", - "server.registered_domain", - "server.top_level_domain", - "server.user.domain", - "server.user.email", - "server.user.full_name", - "server.user.group.domain", - "server.user.group.id", - "server.user.group.name", - "server.user.hash", - "server.user.id", - "server.user.name", - "service.ephemeral_id", - "service.id", - "service.name", - "service.node.name", - "service.state", - "service.type", - "service.version", - "source.address", - "source.as.organization.name", - "source.domain", - "source.geo.city_name", - "source.geo.continent_name", - "source.geo.country_iso_code", - "source.geo.country_name", - "source.geo.name", - "source.geo.region_iso_code", - "source.geo.region_name", - "source.mac", - "source.registered_domain", - "source.top_level_domain", - "source.user.domain", - "source.user.email", - "source.user.full_name", - "source.user.group.domain", - "source.user.group.id", - "source.user.group.name", - "source.user.hash", - "source.user.id", - "source.user.name", - "threat.framework", - "threat.tactic.id", - "threat.tactic.name", - "threat.tactic.reference", - "threat.technique.id", - "threat.technique.name", - "threat.technique.reference", - "tracing.trace.id", - "tracing.transaction.id", - "url.domain", - "url.extension", - "url.fragment", - "url.full", - "url.original", - "url.password", - "url.path", - "url.query", - "url.registered_domain", - "url.scheme", - "url.top_level_domain", - "url.username", - "user.domain", - "user.email", - "user.full_name", - "user.group.domain", - "user.group.id", - "user.group.name", - "user.hash", - "user.id", - "user.name", - "user_agent.device.name", - "user_agent.name", - "text", - "user_agent.original", - "user_agent.os.family", - "user_agent.os.full", - "user_agent.os.kernel", - "user_agent.os.name", - "user_agent.os.platform", - "user_agent.os.version", - "user_agent.version", - "text", - "agent.hostname", - "timeseries.instance", - "cloud.project.id", - "cloud.image.id", - "host.os.build", - "host.os.codename", - "kubernetes.pod.name", - "kubernetes.pod.uid", - "kubernetes.namespace", - "kubernetes.node.name", - "kubernetes.replicaset.name", - "kubernetes.deployment.name", - "kubernetes.statefulset.name", - "kubernetes.container.name", - "kubernetes.container.image", - "jolokia.agent.version", - "jolokia.agent.id", - "jolokia.server.product", - "jolokia.server.version", - "jolokia.server.vendor", - "jolokia.url", - "metricset.name", - "service.address", - "service.hostname", - "type", - "systemd.fragment_path", - "systemd.unit", - "aerospike.namespace.name", - "aerospike.namespace.node.host", - "aerospike.namespace.node.name", - "apache.status.hostname", - "beat.id", - "beat.type", - "beat.state.service.id", - "beat.state.service.name", - "beat.state.service.version", - "beat.state.input.names", - "beat.state.beat.host", - "beat.state.beat.name", - "beat.state.beat.type", - "beat.state.beat.uuid", - "beat.state.beat.version", - "beat.state.cluster.uuid", - "beat.state.host.containerized", - "beat.state.host.os.kernel", - "beat.state.host.os.name", - "beat.state.host.os.platform", - "beat.state.host.os.version", - "beat.state.module.names", - "beat.state.output.name", - "beat.state.queue.name", - "beat.stats.beat.name", - "beat.stats.beat.host", - "beat.stats.beat.type", - "beat.stats.beat.uuid", - "beat.stats.beat.version", - "beat.stats.info.ephemeral_id", - "beat.stats.cgroup.cpu.id", - "beat.stats.cgroup.cpuacct.id", - "beat.stats.cgroup.memory.id", - "beat.stats.libbeat.output.type", - "ceph.cluster_health.overall_status", - "ceph.cluster_health.timechecks.round.status", - "ceph.mgr_osd_pool_stats.pool_name", - "ceph.monitor_health.health", - "ceph.monitor_health.name", - "ceph.osd_df.name", - "ceph.osd_df.device_class", - "ceph.osd_tree.name", - "ceph.osd_tree.type", - "ceph.osd_tree.children", - "ceph.osd_tree.status", - "ceph.osd_tree.device_class", - "ceph.osd_tree.father", - "ceph.pool_disk.name", - "couchbase.bucket.name", - "couchbase.bucket.type", - "couchbase.node.hostname", - "docker.container.command", - "docker.container.status", - "docker.container.tags", - "docker.event.status", - "docker.event.id", - "docker.event.from", - "docker.event.type", - "docker.event.action", - "docker.event.actor.id", - "docker.healthcheck.status", - "docker.healthcheck.event.output", - "docker.image.id.current", - "docker.image.id.parent", - "docker.image.tags", - "docker.info.id", - "docker.network.interface", - "elasticsearch.cluster.name", - "elasticsearch.cluster.id", - "elasticsearch.cluster.state.id", - "elasticsearch.node.id", - "elasticsearch.node.name", - "elasticsearch.ccr.remote_cluster", - "elasticsearch.ccr.leader.index", - "elasticsearch.ccr.follower.index", - "elasticsearch.cluster.stats.version", - "elasticsearch.cluster.stats.state.nodes_hash", - "elasticsearch.cluster.stats.state.master_node", - "elasticsearch.cluster.stats.state.version", - "elasticsearch.cluster.stats.state.state_uuid", - "elasticsearch.cluster.stats.status", - "elasticsearch.cluster.stats.license.status", - "elasticsearch.cluster.stats.license.type", - "elasticsearch.enrich.executing_policy.name", - "elasticsearch.enrich.executing_policy.task.task", - "elasticsearch.enrich.executing_policy.task.action", - "elasticsearch.enrich.executing_policy.task.parent_task_id", - "elasticsearch.index.uuid", - "elasticsearch.index.status", - "elasticsearch.index.name", - "elasticsearch.index.recovery.index.files.percent", - "elasticsearch.index.recovery.name", - "elasticsearch.index.recovery.type", - "elasticsearch.index.recovery.stage", - "elasticsearch.index.recovery.translog.percent", - "elasticsearch.index.recovery.target.transport_address", - "elasticsearch.index.recovery.target.id", - "elasticsearch.index.recovery.target.host", - "elasticsearch.index.recovery.target.name", - "elasticsearch.index.recovery.source.transport_address", - "elasticsearch.index.recovery.source.id", - "elasticsearch.index.recovery.source.host", - "elasticsearch.index.recovery.source.name", - "elasticsearch.ml.job.id", - "elasticsearch.ml.job.state", - "elasticsearch.ml.job.model_size.memory_status", - "elasticsearch.node.version", - "elasticsearch.node.jvm.version", - "elasticsearch.node.stats.os.cgroup.memory.control_group", - "elasticsearch.cluster.pending_task.source", - "elasticsearch.shard.state", - "elasticsearch.shard.relocating_node.name", - "elasticsearch.shard.relocating_node.id", - "elasticsearch.shard.source_node.name", - "elasticsearch.shard.source_node.uuid", - "etcd.api_version", - "etcd.leader.leader", - "etcd.self.id", - "etcd.self.leaderinfo.leader", - "etcd.self.leaderinfo.starttime", - "etcd.self.leaderinfo.uptime", - "etcd.self.name", - "etcd.self.starttime", - "etcd.self.state", - "golang.expvar.cmdline", - "golang.heap.cmdline", - "graphite.server.example", - "haproxy.stat.status", - "haproxy.stat.service_name", - "haproxy.stat.cookie", - "haproxy.stat.load_balancing_algorithm", - "haproxy.stat.check.status", - "haproxy.stat.check.health.last", - "haproxy.stat.proxy.name", - "haproxy.stat.proxy.mode", - "haproxy.stat.agent.status", - "haproxy.stat.agent.description", - "haproxy.stat.agent.check.description", - "haproxy.stat.source.address", - "http.response.code", - "http.response.phrase", - "kafka.broker.address", - "kafka.topic.name", - "kafka.partition.topic_id", - "kafka.partition.topic_broker_id", - "kafka.broker.mbean", - "kafka.consumer.mbean", - "kafka.consumergroup.broker.address", - "kafka.consumergroup.id", - "kafka.consumergroup.topic", - "kafka.consumergroup.meta", - "kafka.consumergroup.client.id", - "kafka.consumergroup.client.host", - "kafka.consumergroup.client.member_id", - "kafka.partition.topic.name", - "kafka.partition.broker.address", - "kafka.producer.mbean", - "kibana.settings.uuid", - "kibana.settings.name", - "kibana.settings.index", - "kibana.settings.host", - "kibana.settings.transport_address", - "kibana.settings.version", - "kibana.settings.status", - "kibana.settings.locale", - "kibana.stats.kibana.status", - "kibana.stats.usage.index", - "kibana.stats.name", - "kibana.stats.index", - "kibana.stats.host.name", - "kibana.stats.status", - "kibana.stats.os.distro", - "kibana.stats.os.distroRelease", - "kibana.stats.os.platform", - "kibana.stats.os.platformRelease", - "kibana.status.name", - "kibana.status.status.overall.state", - "kubernetes.apiserver.request.client", - "kubernetes.apiserver.request.resource", - "kubernetes.apiserver.request.subresource", - "kubernetes.apiserver.request.scope", - "kubernetes.apiserver.request.verb", - "kubernetes.apiserver.request.code", - "kubernetes.apiserver.request.content_type", - "kubernetes.apiserver.request.dry_run", - "kubernetes.apiserver.request.kind", - "kubernetes.apiserver.request.component", - "kubernetes.apiserver.request.group", - "kubernetes.apiserver.request.version", - "kubernetes.apiserver.request.handler", - "kubernetes.apiserver.request.method", - "kubernetes.apiserver.request.host", - "kubernetes.controllermanager.handler", - "kubernetes.controllermanager.code", - "kubernetes.controllermanager.method", - "kubernetes.controllermanager.host", - "kubernetes.controllermanager.name", - "kubernetes.controllermanager.zone", - "kubernetes.event.message", - "kubernetes.event.reason", - "kubernetes.event.type", - "kubernetes.event.source.component", - "kubernetes.event.source.host", - "kubernetes.event.metadata.generate_name", - "kubernetes.event.metadata.name", - "kubernetes.event.metadata.namespace", - "kubernetes.event.metadata.resource_version", - "kubernetes.event.metadata.uid", - "kubernetes.event.metadata.self_link", - "kubernetes.event.involved_object.api_version", - "kubernetes.event.involved_object.kind", - "kubernetes.event.involved_object.name", - "kubernetes.event.involved_object.resource_version", - "kubernetes.event.involved_object.uid", - "kubernetes.proxy.handler", - "kubernetes.proxy.code", - "kubernetes.proxy.method", - "kubernetes.proxy.host", - "kubernetes.scheduler.handler", - "kubernetes.scheduler.code", - "kubernetes.scheduler.method", - "kubernetes.scheduler.host", - "kubernetes.scheduler.name", - "kubernetes.scheduler.result", - "kubernetes.scheduler.operation", - "kubernetes.container.id", - "kubernetes.container.status.phase", - "kubernetes.container.status.reason", - "kubernetes.cronjob.name", - "kubernetes.cronjob.schedule", - "kubernetes.cronjob.concurrency", - "kubernetes.daemonset.name", - "kubernetes.node.status.ready", - "kubernetes.node.status.memory_pressure", - "kubernetes.node.status.disk_pressure", - "kubernetes.node.status.out_of_disk", - "kubernetes.node.status.pid_pressure", - "kubernetes.persistentvolume.name", - "kubernetes.persistentvolume.phase", - "kubernetes.persistentvolume.storage_class", - "kubernetes.persistentvolumeclaim.name", - "kubernetes.persistentvolumeclaim.volume_name", - "kubernetes.persistentvolumeclaim.phase", - "kubernetes.persistentvolumeclaim.access_mode", - "kubernetes.persistentvolumeclaim.storage_class", - "kubernetes.pod.status.phase", - "kubernetes.pod.status.ready", - "kubernetes.pod.status.scheduled", - "kubernetes.resourcequota.name", - "kubernetes.resourcequota.type", - "kubernetes.resourcequota.resource", - "kubernetes.service.name", - "kubernetes.service.cluster_ip", - "kubernetes.service.external_name", - "kubernetes.service.external_ip", - "kubernetes.service.load_balancer_ip", - "kubernetes.service.type", - "kubernetes.service.ingress_ip", - "kubernetes.service.ingress_hostname", - "kubernetes.storageclass.name", - "kubernetes.storageclass.provisioner", - "kubernetes.storageclass.reclaim_policy", - "kubernetes.storageclass.volume_binding_mode", - "kubernetes.system.container", - "kubernetes.volume.name", - "kvm.name", - "kvm.dommemstat.stat.name", - "kvm.dommemstat.name", - "kvm.status.state", - "logstash.node.state.pipeline.id", - "logstash.node.state.pipeline.hash", - "logstash.node.jvm.version", - "logstash.node.stats.logstash.uuid", - "logstash.node.stats.logstash.version", - "logstash.node.stats.pipelines.id", - "logstash.node.stats.pipelines.hash", - "logstash.node.stats.pipelines.queue.type", - "logstash.node.stats.pipelines.vertices.pipeline_ephemeral_id", - "logstash.node.stats.pipelines.vertices.id", - "mongodb.collstats.db", - "mongodb.collstats.collection", - "mongodb.collstats.name", - "mongodb.dbstats.db", - "mongodb.metrics.replication.executor.network_interface", - "mongodb.replstatus.set_name", - "mongodb.replstatus.members.primary.host", - "mongodb.replstatus.members.primary.optime", - "mongodb.replstatus.members.secondary.hosts", - "mongodb.replstatus.members.secondary.optimes", - "mongodb.replstatus.members.recovering.hosts", - "mongodb.replstatus.members.unknown.hosts", - "mongodb.replstatus.members.startup2.hosts", - "mongodb.replstatus.members.arbiter.hosts", - "mongodb.replstatus.members.down.hosts", - "mongodb.replstatus.members.rollback.hosts", - "mongodb.replstatus.members.unhealthy.hosts", - "mongodb.status.storage_engine.name", - "munin.plugin.name", - "mysql.galera_status.cluster.status", - "mysql.galera_status.connected", - "mysql.galera_status.evs.evict", - "mysql.galera_status.evs.state", - "mysql.galera_status.local.state", - "mysql.galera_status.ready", - "mysql.performance.events_statements.digest", - "mysql.performance.table_io_waits.object.schema", - "mysql.performance.table_io_waits.object.name", - "mysql.performance.table_io_waits.index.name", - "nats.server.id", - "nats.connection.name", - "nats.route.remote_id", - "nginx.stubstatus.hostname", - "php_fpm.pool.name", - "php_fpm.pool.process_manager", - "php_fpm.process.state", - "php_fpm.process.script", - "postgresql.activity.database.name", - "postgresql.activity.user.name", - "postgresql.activity.application_name", - "postgresql.activity.client.address", - "postgresql.activity.client.hostname", - "postgresql.activity.backend_type", - "postgresql.activity.state", - "postgresql.activity.query", - "postgresql.activity.wait_event", - "postgresql.activity.wait_event_type", - "postgresql.database.name", - "postgresql.statement.query.text", - "rabbitmq.vhost", - "rabbitmq.connection.name", - "rabbitmq.connection.state", - "rabbitmq.connection.type", - "rabbitmq.connection.host", - "rabbitmq.connection.peer.host", - "rabbitmq.connection.client_provided.name", - "rabbitmq.exchange.name", - "rabbitmq.node.name", - "rabbitmq.node.type", - "rabbitmq.queue.name", - "rabbitmq.queue.state", - "redis.info.memory.max.policy", - "redis.info.memory.allocator", - "redis.info.persistence.rdb.bgsave.last_status", - "redis.info.persistence.aof.bgrewrite.last_status", - "redis.info.persistence.aof.write.last_status", - "redis.info.replication.role", - "redis.info.replication.master.link_status", - "redis.info.server.git_sha1", - "redis.info.server.git_dirty", - "redis.info.server.build_id", - "redis.info.server.mode", - "redis.info.server.arch_bits", - "redis.info.server.multiplexing_api", - "redis.info.server.gcc_version", - "redis.info.server.run_id", - "redis.info.server.config_file", - "redis.key.name", - "redis.key.id", - "redis.key.type", - "redis.keyspace.id", - "process.state", - "system.diskio.name", - "system.diskio.serial_number", - "system.filesystem.device_name", - "system.filesystem.type", - "system.filesystem.mount_point", - "system.network.name", - "system.process.state", - "system.process.cmdline", - "system.process.cgroup.id", - "system.process.cgroup.path", - "system.process.cgroup.cpu.id", - "system.process.cgroup.cpu.path", - "system.process.cgroup.cpuacct.id", - "system.process.cgroup.cpuacct.path", - "system.process.cgroup.memory.id", - "system.process.cgroup.memory.path", - "system.process.cgroup.blkio.id", - "system.process.cgroup.blkio.path", - "system.raid.name", - "system.raid.status", - "system.raid.level", - "system.raid.sync_action", - "system.service.name", - "system.service.load_state", - "system.service.state", - "system.service.sub_state", - "system.service.exec_code", - "system.service.unit_file.state", - "system.service.unit_file.vendor_preset", - "system.socket.remote.host", - "system.socket.remote.etld_plus_one", - "system.socket.remote.host_error", - "system.socket.process.cmdline", - "system.users.id", - "system.users.seat", - "system.users.path", - "system.users.type", - "system.users.service", - "system.users.state", - "system.users.scope", - "system.users.remote_host", - "uwsgi.status.worker.status", - "vsphere.datastore.name", - "vsphere.datastore.fstype", - "vsphere.host.name", - "vsphere.host.network_names", - "vsphere.virtualmachine.host.id", - "vsphere.virtualmachine.host.hostname", - "vsphere.virtualmachine.name", - "vsphere.virtualmachine.os", - "vsphere.virtualmachine.network_names", - "windows.perfmon.instance", - "windows.service.id", - "windows.service.name", - "windows.service.display_name", - "windows.service.start_type", - "windows.service.start_name", - "windows.service.path_name", - "windows.service.state", - "windows.service.exit_code", - "zookeeper.mntr.hostname", - "zookeeper.mntr.server_state", - "zookeeper.server.mode", - "zookeeper.server.zxid", - "fields.*" - ] - }, - "refresh_interval": "5s", - "routing": { - "allocation": { - "include": { - "_tier_preference": "data_content" - } - } - } - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - }, - "index": ".monitoring-beats-6-2018.08.31", - "mappings": { - "dynamic": false, - "properties": { - "beats_state": { - "properties": { - "timestamp": { - "format": "date_time", - "type": "date" - } - } - }, - "beats_stats": { - "properties": { - "beat": { - "properties": { - "type": { - "type": "keyword" - }, - "uuid": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "metrics": { - "properties": { - "apm-server": { - "properties": { - "processor": { - "properties": { - "error": { - "properties": { - "transformations": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "transformations": { - "type": "long" - } - } - }, - "span": { - "properties": { - "transformations": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "transformations": { - "type": "long" - } - } - } - } - }, - "server": { - "properties": { - "request": { - "properties": { - "count": { - "type": "long" - } - } - }, - "response": { - "properties": { - "count": { - "type": "long" - }, - "errors": { - "properties": { - "closed": { - "type": "long" - }, - "concurrency": { - "type": "long" - }, - "decode": { - "type": "long" - }, - "forbidden": { - "type": "long" - }, - "internal": { - "type": "long" - }, - "method": { - "type": "long" - }, - "queue": { - "type": "long" - }, - "ratelimit": { - "type": "long" - }, - "toolarge": { - "type": "long" - }, - "unauthorized": { - "type": "long" - }, - "validate": { - "type": "long" - } - } - }, - "valid": { - "properties": { - "accepted": { - "type": "long" - }, - "ok": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "beat": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "value": { - "type": "long" - } - } - } - } - }, - "handles": { - "properties": { - "open": { - "type": "long" - } - } - }, - "info": { - "properties": { - "ephemeral_id": { - "type": "keyword" - } - } - }, - "memstats": { - "properties": { - "gc_next": { - "type": "long" - }, - "memory_alloc": { - "type": "long" - }, - "memory_total": { - "type": "long" - }, - "rss": { - "type": "long" - } - } - } - } - }, - "libbeat": { - "properties": { - "output": { - "properties": { - "events": { - "properties": { - "acked": { - "type": "long" - }, - "active": { - "type": "long" - }, - "dropped": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "total": { - "type": "long" - } - } - }, - "read": { - "properties": { - "bytes": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - }, - "write": { - "properties": { - "bytes": { - "type": "long" - }, - "errors": { - "type": "long" - } - } - } - } - }, - "pipeline": { - "properties": { - "events": { - "properties": { - "dropped": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "published": { - "type": "long" - }, - "retry": { - "type": "long" - }, - "total": { - "type": "long" - } - } - } - } - } - } - }, - "system": { - "properties": { - "load": { - "properties": { - "1": { - "type": "double" - }, - "15": { - "type": "double" - }, - "5": { - "type": "double" - } - } - } - } - } - } - }, - "timestamp": { - "format": "date_time", - "type": "date" - } - } - }, - "cluster_uuid": { - "type": "keyword" - }, - "metricset": { - "properties": { - "name": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "timestamp": { - "format": "date_time", - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "codec": "best_compression", - "format": "6", - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/monitoring/beats_mb/data.json.gz b/x-pack/test/functional/es_archives/monitoring/beats_mb/data.json.gz index d725c2ef4c46921930a1d682f9c8320dc4d8bdc8..7c43645d84175d7bd9005d50db77546e4cda7169 100644 GIT binary patch literal 643153 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

Gc$8!a`hLMk=}R| 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 z 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 J+?Tc3{sRn!obUhu literal 553234 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 K?q@R=gmZX}duz@bVb%wY ze0F-t@1pyB?MeFjzhednn>v7`FzO-^aE<>x zpHM|xw);Xg9o z_Q|Tv8syCnzkRd`!4bH1qB>I>#oQ2j(I(L?echHzzmX~!D$?wxXd~&`FKM}dqu1b0LOWu}Pg6F1$BxOuHHc1ZAyOU0n+CU~7)R7*ZCUP69LyQCs(E-+VGfNJ+WGWer6SGi* zjLle!pAM#VgK&t6G4zNCab2!q&O}$lOoi!{;VUb@e9iM43lo}s^QK>z*f&lsBzYSo zX@`JME&VB%Z~nJU$fSIUKT9!-O7S|$8=T^@B5ySErD6BQfPqn&>i!PCnVOgDun85spBnPOg zjl)S}peBG&5mS&8u*Q`OqBt^y5`e+xiQz+0wgN{wVoNt^8?%W29E`vgeCCw3`e`Cw z=Q~@2%RhyMs5JtF3}@ss2NLe1Lr}9UPla*Y-aOXD2&69YBW{fg_x3!R6Q6r2^GVFI z0d(xz&yuZNCeA0wevx~Ig>z;g@kKu{ofumpSezw73^xO45*ihX@LIVk;sVwn<-!(x z)EDBKE-4tGJ&X8W6paM@5z6-w!WM?o;am!d(pcJW!}to9hvmX{DPMi;#X@0Jj7;k3 z^-l0@0!Kku6If>3X|a~YLCr;s;?eV2sJdxXtW36it0lJhQp+jDVliSIDojjVv#Cu@u61vBi#h?4pa)S% z@bBU%GojO7?M`CLujG;5S4;p`EqJu@NT+Cs+)4AyK1zhFh2o_~O{&?5oST&qll%7;n zv0F(MIEJQi2+P<-f)O2X1TNPj*?0=sTt_nb{$Anf^oh;eYar>cC_To*JDe#s_!G4Y zSCvMci$;f<4=HE_lpozscXM%!b&Esz=Wb| zsjN7K&OkCMVdXD1vZ!fLh!heaC%b1ItNt!h@!k^8{qgWhMjGI%ZS&$o_rw}!#UoTm zuIE3mq2>+rL=bB^swK@*>CAQ&Lmag8FT>(gZVxR*py#&Kc}7o^u%$hILH_`QDxN9W z0$>pO(BhPJUo>e!8F(bR%70L6p$rF^)2UKa3?ktAL>e!=b{IBT(%lwQOYJ_K({zy> zIay`%(2`zqKSTPriRfHC);G(<471mCGH7E5==Lds6|3}g7i4zikR4JHx5Zv#h&&Oh z`teC~OcNj*c~ z#zCrtg8!re>kWEK#Ojc}PafKxR58>_Ba$!Uja-?O)Euw8CXKG{?I3?W&2SNHnR0Pb znA`-}()*++;!Y`83O^MF7}!M6TbyPnjvQaIIP_N~tWUKE7{ItPIw*hAP@_w@}28%{CKb>rIIWEdG{wrFyvivqEh`0?X^*)-nQZ_JGPLO z0#j@1a1x)=S#y$MQ93^{E0lFFqC9c7ouw@bk?yxd=*-yf+N-UInUnkx4^rcv{Tm)+ z_!l1Z@ee#m;2-gzwZHHni0f}W=-Xd-knR5o57OTFyGf`3z;VRrl?bH+xc$^c954^K zh(9N~8uMK3OPQ*b*|so!N_Y=>!9?>Lqmc$YLe>AuBh)q#J;25xZCP|_-)0*`ubi|? zT%@C7dIl$v=um&dbL|vqGQy$FrWaMnn&i?T7pCSfP#vrupgj465<Y=}(L81RE8kG1qG^jp_nYUCSm76D8U3| z<6ymHV07NXycPdLCFJ_+|2>ruk!@Er+A*-eycc$||4fXAfj4b>y|c~3yT+cZ)TX9h zFOpcu^x{C7)Z=qTW;_fv4~#0mkH2Sm%B)yls)ZO`6TP~Owv>4|rM3}s?1GWBPSKbh zNO{qZmJWtpu~bbM9*_u4Hjcvp5+QLwA|!A{gjZDvBL|{?ln8kN5}~{3M~P4pKOhkj zHv}X?4@{2|A*DrCR#`EWuS9}7xoKY95av=8V7wk?( zgRjfqr$*Iv; zSOIgSeKK{OF{BX=AuN85cE?YZzo%!HKO7C6dh4GrrY6cWNvi@>;C*<+pe6iTw&8 ze^xcNztz>?^@QV4nU{=(?aJLfbY1`a?0dc!N7~o-*1mXI_0|&`p6u6?vI5D}w_ntE zZcGX0S4yaw9DnRtJ(Qonv391q_}Z4V7p;IUcXC?6$7dfU2($Er(2l4ODLIBK+<>R~ z`lh1oYYn&C?eobW16LP2qFz7t-I&ZPTJ4)x*ORd4_BiYDMPAUHk`I-?6p!V>{4Py* z)6;i*XQUFX_j)=TWWh3j%+G5z_WwIDh{^2#BQVGw`ODb;!7;(*HdUUq1;H<}KCf70jaR%OIWmA9{RlKNTGmj_N|r1+ zAId5)kK;|I`CGV!0-Q-3R=$Q zX99T8|HOip|H6Vk|8H0j?}lxMUq$5L9pXS{|G%VyV*WW5f~EO=D^{Dhsu4KW(h$tAU}1 z!-q?e@)P&@;0wQG-y(h`pZc#XLUk13nptNNl^q@Vk4Tb{Bho4PS@DO>uA{e|H2)11 z^f`yN&DGN{b6W#c8P^EH(!iBr@T4>~ec{4cpDeDoGRk<3Y>rsFX0tc*9Jhp4_aG{L z47P@}0);|@QX@P_eKG{T^Xg{cPIIl0R#yfMjkG1<*UQZYYAU;b6<&MRSn1=3Z#IVrymWpcD#2?&;g;)eu;z%hOS@?$Y1S}Ycpt3OK-ab*;j;+k-Wx1Ykt zCjeb7+wJ4Ezp1-# zZf8Zteu9{qw=9_Bl(#EBQa)>Ci|*cYKhL&y`|uq{HnV`xSmC7}T=t!xgq}z|-SMjr zXLo))kDyL}H&n9Nm&N<;TK%Z?HW~NdYKR9g!Du6{<%cx>qEnpnS3-vy7ltyLj>#i zoAOc)aPmx!!IKJOg8(f!YrQB?5lsjI!JWnm&I{dis|VINrRylVbQrJ8~-5 zS`XU#p6!5|P&xu1v(GA|plTS*gbuPt=zG6pG={X=%DzOoVk;w6> zlR8xZA+v?DR8kwgO{20j2=VFU(~9yxup(4^>b8y>r?*~X0^ck)z|?slD@fcpWgG^L zsBo~Rnm)+3TgTi3kPvwWgZWVZk`N)lr+csbbSzlaoCcv(mOzG@nY1_I8?Q4x#8C$CIJC*8^64- z%IbC|pDIrJfYJ*W#0Qv#3z8 zB7xhZgUGc~T!TjhuO{2bV^5w|1q5I{6vD}Ok>BRdzpa0`ntcmaleUeQ_oL6J-KHY6 zQVzBpGf4xWfQUb#B4c&Dm!`&)Y9qV{6`O0ZbO2O@2PvKP>zk|ES)81S{v}bG07!_? z)Qe~b(nubmBEXcRE+HC!K(vIXP8$0JMq3203&s4^9=wpuBUA*u`u<|y{q2N%PE8dr zwVJRNM$xDS9&`5@iyF}rXvHX4O1wXU{-d(-HVn7b#Y-3&NdW5!2vXbd6}&#(Gt%X* z$B71_Pi6QKQiv>?O)V*?c#jlStF$-`1zt?eB7fwJnnX?7@q%b!f~rpQ*`5k1kvfW6 zt>5S%$>PmI<4gCG8?~_~1whM8`LC84z!Zf%w#;Pz-ZCqkJpA%k%k1FpesFznfoOAO zjRsg&T2@u9D9%Hdj5d0ojv=0XXM9v%)+(ijEfhZLql=@q@mI=hEz)+Ob0?5`EwKo{ zph)8||0{Vkp*lp$H0`NyG7yd`?FC3Aq6)EmKpvt9Q{FnN>9Z4p{l~|YT#z&LlEm7Y z!Ff+ZrnW$ESnroL6TgjStfm7M zN}paJ&_tjyqcjulWt4lD?dDT0Q_{>Frj2C=8=lWd0o$4`j>ib~Vlhsv1+dkjcv5ECmpzFAv`&05ylrQ+t3HF{R`-sS19WPWoWtQ=S) z&EPN)MgT4cxCDPzE9K%(X`K|wfN9R8#N6OMTMDp5>O!p|tpNy{F$+%wEy=TwKYXsn zs;0m|7gm0QWvGCu5@U%ZJg`OmH~CO{%m5w@il%qsAP~O(Eg#D5NhWD`r_~+jd?6_H zN;n;?ru|pU3^*b*)jj|*v&a)&8qTc3g4YrG&479+TuaUIQ9b16+<9iFEBVL~X-S#h$sepAZw-7| zCHWv+7*KcPV=6r$jm$=i#X%OHr7FkI8>~w(Mi;;{Fhw`VqWPhcLNr$-HvmD+?S1oe z-JEwb=4vzersAvlgmK-Z@NloQriEE^fhuAt*ovWSSxQZL{ta)C%`?1LlmJjD9ZXOD zy!vu-N>SIng(Rsvs$PkdK?E;^NFAmzp!RUegZ9iMHzPk{mJg&J6&FOm@!5#pfSmdE z&z9Lh#N_P``IXI8v6o1x$iUScu{y^b+jJ=#DF6{+sRtif6rd&7Unr)Q{Dz1YK>$Pq zBo{AJb8_D2Y&QPXxU>_a7gFC!h>t+1s${3GEZXv5JVS|-N<~5dAR@~GDiS1Z1%C>` zdRqOrQ;!f4@QvX8z8y# z;b;1QdX6^frt2QdaQhQ|$LZQlL-W&5x$-|j3i98mYu$JIIQ-4>XZ-GUFqUA>I(6hBHn zK+eTjRP^a%%Iq3QnE?n9kTP@n(S4;$6UceLbwsR;25>+f!`;2gEu09}o`y90r%JmT ze-K1e!s3pzs=u^!uM-?Hz(NT0T0(nyrkdoPz8{x)?oQ){5MLMJDTzBphyVx2GXeXw(CFLA1w|fi1bUQjkIJEZTxWVY zWuhnI`X8FJv+|ATd!zOLOqnex{w-xDp5^Yd{?NM4wmCJNBOqehPV90f+LB? z6#I>gO)s0ON#Vs5K9r%RBu+G#2NAu*#&xA-GVo~KN2>sIOM8(tnxZum^>R!P5DtX^ z!XZ9SK~nN+3)px-??t6|`cc_-?@3eArD$saqGoff9rG3Qm5njOt=OY)C>2XnNDCSO z_=ZBjS7m8$;3G}r2XzcH{00=;7Pu?5#QR111A?@cS21Y}rfPhu-yMpA@uU1HlyOl& zVd3K1BmUu7M!+wy0wX7ZmRzO%fD){(FUQ|>LukZ$yl#RGC96m0kz)nUy?8;V6q;&4 z%#0baBnyF>{V6J3%4Q&-8&dekm|4jj5Hl;bPp#@$!N(0}6l1bjYqr;16l8D)mV)fk z070_axInNK?EE~KF;$_A1z<8Hj!m!ueak<@%;^6fGaFkOT>o^gEqW!;hyZLn4d6q6 z*33?S*UUJZ_k#bJ3Yg?>=iFmBaIl@BnlCVULyC0XJ;u!FB5D6YGz18amOkIKKWAWYH*z1tZZUNE4G-}c zN3*d3@Q`3&dd7>hbO3_RqW!z&yR*;r?Y_e( zI<(ikbd6d?OsZ@cKuOC{G_s1bQOeK4B5%$7A-pE{1{+KtE;}87sPomXrsXp4dhcxM zvv~l1l(yJH2>wvhlaQ1x!?kJf7pA7+GNF)EV1nQ3R298|5&S50Qk$RkSzh63f4S*( zKD|m|B`wc1WtNgpJsJLnC$*bFm>)adT{KD>|K>K|YlY0EW;L~H&&*f*ff6S)Tn>YM z`~`L2c_vLwEiM9@L$z9Gl3uR8srA9C;-hB>G||lPq3_CjZ+X3Q9ZcJk_7S6F*an^S zQ8Uyia@d`~xYJWZ)1+O1qZ*YI--%bi6zm7LO;dbhgq9AJ$Z8#Ar(9RfKg=0~G6>#2 zT86R!%TT=P0IiwIm44Yn`6vkxumMVBhI#mJKlgvWy_BSj zWl}~Lv7&QYHR4*eq7E#-z#O8CMH`5dX>Mz(cbyg-SgKpG!e6Q*B2+TfefXjD2o8|~ z;1DVZa$UJ_f3u=cF~osNM{C_54ql270-zx#c$Cox;3y@#38zS$voXR%6T=mfWTfB} z14TmmWd0r@!-I6p@~m{+dUzOJ)o4IZ(M0sOn*B6e2mUYC-ZHAKXi>Pui+k{5#jUt| zu>i&0-5pw>xO)r1-QC>^#T|+jiWe`%-3q)-d(OFc-0zL=opZ+<>nB+u?2u&dthwgQ zsI{}xnzZD^FrzBC`B z@T;F*(%;k9YZ=`vKMv!9#l47UZ#N9G9@6F?hRrT~9Pi(<8a0j?u+LwyCV79ROhtV1 z$tAbIX;H?qHG}B-3k)uUx8@$nC=Kq(ftc5w{u9I9aYlZpPePP$FOm=}(x*0_{FN+> zcojEMvcUNF^{(E_1e|AU$CUSt7R$e+*K~GQ#E+#v;c4F2B|Z)NaUW@C`?4uWsr0nv zkU5=wloZ)KUV1^~uW0SYvD0t*lku|#_u$*MwqfqppnR$KX+pdBX}kH==?h`kFTOsV z*hxE0PdLN#rEkO&Z+7f{O#S@n@py~}HI;P4I@h@B^=#Xa(^$Pny%7EGU7e&KD}UD= z#}}um>eHL+9}8J8_$?=&0*_|F_rhLK0(KbacdbsOfL}<9@47I0{zCr>mGhl~lwrjB z^kONi*G*$EGXhHj>vD*_cJj)8l;zWMBzS-4snKiYs)CT%8EYCj%atnBK;r5rZvui; z@wfBcFG;1;eouz0iV4>mau^FQ8dda}@fCfzRq-q8dWC;Oc2m?B)Fz%*6w=|( zzc`b;w&s(~C@0TvLNmuOg*rEIDWlXr#SNAYvfF8PD-!5^F_rnla88{W& zd_Nbg9k|{R_N9oe%w{`vo-nPKsFlT9#r)w;!1na|+g)#( zja5z`rjfv=z5N~SrfG|b~WO~VWDbG zQ7U1W4XTK2C)>o|!p4{+_O2n+GTSBMN(`y1>x{_Xtk4n@Jv5jfipuYCIudp$*2g*g~tfgG}A|DQF>S%FWH)LX9wH-{`yZs=~ka`qXjH%L=!Gk6l5l+1tbk)gsnGE zR8R)b}po@0*l;c^rNS1Y5YMjlmYP_lftqU+CFm) z$A&z%!gn_2FSdLF>*1kXfozK zfGgk6m`Wba@@8bVt60}%d`oWFGdPxB{`1TKikKvn{q;AMu#NK;04rLW$VhUN=qYX)L=9>@0GSjTR<9VXf&DPj58e~)} zUNFPjJf4iFruBT=g4TeDI&JtCr z)bmNI%Va(kWaSeSZf;97)zf-m9mTngNf}W!pAN5<&6+vB=;}8C*dEJ2_|PyVAAk?B zN2)RS)VgB7GSrKnl||QG!I6aEfpG~dB?D7mr7nfLG{_jkGlHYGFtbOQxfoTW)bQ{U ztTtEJktl7U;I=$?l(3L}Irgnc>5(2^aFt}xbfTv`?2h>`}=si=c8ytB+CQrGL}P$C^)9Q={| z$ew{GaA_&@c^nH7Y08h|al2j?Bl5&i#cc@+gAq=r3z7=W>|vvReSf(ZZ+1#We?}Bj zd=6$W#7l74T=|O;38~PaBo4dLmAYN2?S#L;E0I4_6CCqAVPLH>5pJ|2H%^nDXeU66<84gCFR zTQq4DL#y>P7z35AnU!dA1Tm?tJ@5V9f+d+ETw-q%S{1s4_NjI-ERzq1t3d zK=I7cgc9p@NEmZ>ryD29PAQREq}wpZ1yjH3u)X)Ki$PVc3VS`BNTKBtRZa!8Qs;Y) z?G(zH#N-0j?Tw!0h%?9FX+@k;gFrm4;^A{-ljR-{A`{8@JeQHdNMK2I+H+;uiKM~G zKcqJ=0jp{FW&_e?9?{`|yM-(Ef$*!@X%@Id3s&tQ?&?O@%g!xrzW3xke>dCZ)Nzz# zU)l%eZ!o)(E&jT-%#^7oBP!}vQ9jdrt;HZd zbXpfFAv2LG1rdQ=ZuzX1oNDaADmZ{YoEeGF62me>wAX?&z!XZ=AwU~oh9+7kO`iG! zvUf)pcyZ9A2S5JX#Hr$HhD`nMc;lbZ2H*-Lc1_!9(kRMpx_U7hl zji(vQN&W9@oW}xml#(TmC;rIa4&sPzDfOG-c_3IgOw+GZfkqi}#TEn({QL*~=9USO zr-zo?OpA83Q)s+`ODk}j?rW~9%^N$gcSx8;VUQuMvBp-IgQ5iuLp*gxDTKroddVu{^IMTL+2naAXVv`896Wb}WhdTCjR zo1msQ9@l0sE8nE7^O~XMaP4)(E8DKP`@T4M66wd{h(aR^H>>QQ-`1h zE*hq+kOw+tyw)$NFg#pQdgg~Df@Y2ILSJ+hZfdYr;biX(5XeBK(~N(#E=OBuCsMk9IP3cQ);iU^0s;yJ6hTN$_#G1!azxP!2jW& zp|U6_wZi>#W~TW-Srrym@ha@Z0ywA003!sM`2qqy<9;$eTiH@ zMl_Fl#w<)2+gA@*!9{PUheeL*AH3Pvl{;L=sn|AcEu|F3cy&~qhe~R3aoL$Zr(vFi z>xiE^fR%0PC2LU5z<9?Hi7>95UQLHM!Es^F6UxN#u*6uczMXEHd&doD`4r zJ+VgKC{_w5N*T)50tXU)`KKuS;~3xQsE1Xdgs`ZC;gz>BQ5=@tU!K@I-BowJu6&lH zZ)sOi#Yr9u7p&3K20i8Ou)t3F%$qq`(Kl-s2Cj=O!da)vUwG_axeD*GtrG1$We|FG znc)vT$XzvyVAM{7OIw^(^B;E~4%T=iad&Io>c}?hvfGupj=c`;21m6HB6i2Xg{|B< zS`)8+pICk&tXvtG%JEs!OY5u~PTPA&6m+#c-mkI_kKWuSL%(A-pt#gx-yjgv`|)}p z2!b8WNnV-Rc?r)^bE049D$fY3C&xXA@d3DyeQIPOx_-gav7W`ckIXXA%}*2jjo~Ls zjAFa?vHPOkg~e}sZy}DSn;RMqPRti-axqr z?}iPpYz4A;^<)o=dOh`v>6%WOSEoy_9MF0Wbsv&gP569@7@JIOe0QC;Fir?Puk!xu zqvC|d#XW4uBuAC_prU#E6;A4Pbzv!vA3TAvmyr% zTdBBSH)LF%U{z?IMD9tf4v$ahzN9FAvy6o0CJPtW<&ArCar9MI+pjGkTDW3nJaLwc zS=^{~w8kS}ive>%I(fcBkl<}_iYG?@#mT*!_R`|*)%lP8W<|rI9fS8Bhw-_OJf{1C z{Pv`oWN?ExJ#(~TsMr)I*|1j&;V!=KIxelp9ekGW%MjnH0vX$S&LRyddC@;5scG=jjA=>d3$+i`*iTIGdIU?j0B8f z60I|4Ibo*LsE8u08jCAc!6G7yxtBi|&4_pGtij8#kqBH);4cv7K zy>9IF+nUo;Jye}`rw`ICftZ@Xj9s90rOn9qBz_?0n-6B-2?1>2}~^JKw?SLm+xGE>Ey6$S-&`^H}!jY(YB`Yl0C z^k65tr)2JC;$BJSC(R02@aK(0O|$K7&>Mhl3hdYP8s_zm=VcXl5vj;Lq&t;jiGc?T~}h|Slv@;F^raR)7c3qEq-)Pz_iGvi*b+vCR{yS zJ+@}#*TQ^$^-}g>+Q#m)btqx38S;>Rq{Y$O%uW=grvsK?Bzals@5J&^L)3;rc0ZGC z=DNgXB=WiMCMM1(u(syw;n(SPFl&=;1y50#LTTQg!(B;0!TEv|D3k%q2_S1#4SlSe z4Kb-w8K6)mH@hKpuD)F>sZ*vQw;ULv%i_0k@cVn~!4XE2M!4=!C*;{WG}uci4lDrmb7N}XhKA_H4~akjYHpFrb?)!>t~RANAWu%u+z%ifk^rPbE=YW;zl%R?-$Oe~0w_*kfSj=XDMmXZ zbF1$hdB@h%qx5qr>@&St*xF@zklDhf%VSFo=F}RKbDA+18&cd z0^W~n|B95+wV1taVEN=*T7DTyVQA-K_}n8ylI@Tfbcx7`toLoqJ=;F*#`;SJ?I(Vl z*!H?|KAt}$eZHCVFh}dwXP@sab~y+*wjKrDR9^6iFgVAxEM@34TR@{stebjm{GO-d z^0KmfVt~aNX@LDkQc@A>-4sq?Dl&s7JBe0)UY#jtck-(Pc{2*`397_Tm)YBi&FwY} z?-eBiLu7*WZ_9)YIvr0sX(%ZQgA*W@4Ae0S=N|}q#ArT2aS-10554NulqYL~qf%{- ziW3xP6=#Ly>4Cl04yyDU96o7+NyW5BJ4+ITOq_#Jj>zF;rC@M(TU{_#znpQ9A*Z0}?x8Zg$4`%m3jHoj=oStx2_{0Kaz&)-9ad6W2PGy; z5ZBBRlCmVp8{{G?!kiSGx%GgVDcHM3Y3x0HZya`m4!--S$d!Z>#SJr3#D_QhSo?2v zGI6uIQ`7o>#n-c|+dH5*q*NeIW`L9`Lm7`f1jnFAvjxP-#KdTVIzE-}FYT72u4du< z|HlRSW;Wt`m|W`Wb`@nN z#db`=41BxZK-Gs$5^(4Q!l^frIaKjzUcH_#SRz*g1G=G*GL;0AF*F-BtYrqQP=@^6 zq4VhdNQRGt2>wjDk%qzbRcFxT)X%}Lni2+SfqP;`pXU0;cG;4^8UoqGN(MduZR2N2 ziAJJiY-Bh{y&->~587e&mpfF#M;RbPja2IG*KYZ>d7e~QU;mL(rQ513gi-~1O#QIZ zTpT{``9n`-3P~npv;5sx6VAjb#v-pv%+jrdq)P8rTxlr~3bxG99E`_1H3iIQbhU8>a-r zbJD7u600*9s`O)(V3rs`CuU!dZzevp-w!yXGnD02l&B=!zjE4rBw(wU#{#-!q`LV~ zvBhPbqRH@E(I0U&3G$~IphtqMI$J1tpP>po*Ta#Wu>Iz?`G*@YWnamT~T=%?{l-`V?^5Mjmh zhAbGJl7l!_&nEcMaKeJxj6yL zkXtp8-dJ8L6s#Egs2Ls5_Suo$#i)Q;bwd*a^x#0USd@BV zL5zBS4;SPjzJh+|0X7SHf3jqG8Skfmvg#3{^~W%l0a>!R{_D#4G^c(tAWMevH%nFn zH7fhGb+%K~E#h;F497%pnK-h;2|JSk#@jLiUX4JOOj23byJSOxsf?Gt^tWsmpP^@S zWj-7QC5WkDTK{lGza!x2c6}E*4*9QRi1y`Bj_9NZm5Tz~JrQx?7`%fOATj@xK} zPN6F;srmc@RLLFL+b-U3{-Qx-$RcozsLqNPX)0OL! ztL3URJ?@<~*nhZG>em6{f*`7bx-WIjx7+8HFB^=40Ish@WueRoyP<9(r6wtKvv%l} zD5X9T<_}wff|!qX1ZI5jfxn0HJT8S8pDTG>tYKZ<4HbYK#)Z7L~27cbVFc1r+kgjg$6m>z!d^FxI}fe+}1W)mW>xOMZit zWR1=ptvz5Gx6f3uI<{fPRIlWq)D`d=m&=02jKT~Y%w*e~e$}yhcje&vL`;D)uyOxw z-1vQHRL)q=smyYV2@bCoD>oARR16v#pVn_npfM*Fik%XnQlo^7b70}R8S7*Hd4X0| zOv^e1pHPeUSuezlanL778o6wPatQ~b_n<1FQ}_@`X3J=Zt)L1QE((uQVg-nK^vub~ zs=o6DJYz*Ukz2SSK#nY$n;XyzapLlooTLSg%;38EEt#7ls(l`O)(b&8lAh`e%*|8CzlD_qTlKbhsP0F6L+sB-w{B)bVrGk zm`Y=Gs_Jlw#;Ac+i&<+#k<{3+`R4v|WjyP*XkQ>3CRHSi7z|r29U~trmiQlBuy6+^ zD&n$|fWc`0Q;}NdF8d`Lm0ou|qFeiUQ}c(}+k;ia|1BE?#Ki8rcYD9-Y|bR=v{Tst z?Fo1^dKp~von;*ls7j^*&0)TmruvOHBT?GU5Iy7g2kbXf@kCW#bMR% zl%lf z-`WQrTgPwdintWw+Md~al+q|O;jJNZ;|X;vL$haJjkcB{HO8@$gUr)>q=U?nwl*$5 zMK0c6wo0l*hqu6^G*>uguDBg77N5CfGSua`HQ^Sb^97~rw9KUqi`o!kNUf%6e@oTD zHH~AVq^4WpBbVwcBYQL#bM8O7(bX$6fn&iDL=pL{c`RCeDaeEbt3v#d~-LI5IU zmaHy~)Gr(8+$K#zqJr_txwvevvTt-WMI;K4bP#oF!kHP#+V2g=PQzv}m;>=-eHDlEDUU-5;+Zb4C9ms*ei}5t)_wj$@$n4}I!-EqManz*&|Mxy z17mtb+#hzvlp@;eb^=uv7=Kxu^ICIBg=1!&lm!&n=w33jK zwW2~i8qsj2=O^1iF97J}Inij?LIZhdp)=3z7)GOTop7BRrP1~`KaUQok2BL%^YEf) zRkoV??V7&HBdG6_eSAX-ilj)ibG0?!&+&@{ltSN_s{Sg49`|y~)*h62#j(Y)sbuQB z2If`)H!~T}u0Svq!T*j8IT4MHzIOLQaJ0fEcJ6`<6Csm%6DF}awiJQ^u`@V_?@puN8c?IFNC^O z>iongn%m=^IKftl`hUZNh%+lql?=Y~S@Q@C4#tSRdU{}~GqfG3^u4I$N`8DT;4_Dg z|33jC@^_OMyq<%3*{9csu;WY|GH<>_|u0=zb6#S5;!xt_vzZK*yU zekZ>%@B>{12I033A{39Ju9E~TT2Sr?X&njJ1rlbW(_1^0_9^q(zAh$eZ0i{|?O;gl zYWnTqv)r+f9((IqEM-j)UVO|57g-vjc+IH`K56ipXPDR=l@DQlUYp2*eofS|YY>H?#Bk#K zFEHqr+7kj1ZQ;g%5YEPg!|OAss&}0Gh(FyTf$*5C;Jt=|LDmB~$Dkm;r3+R4pfqhW z|Ml_H=zYZ5s?8q7eU3(1`L=2$K{1QR_cgZvg9~y))v;~h7Q8u9Fw*$f&utU#)V>-EC$`^4?2lvZwOU~t^##W}~` zQ|;{CO#aKYZa2u{YW%~B1F06v%T>1tOUk3SGlO|dR_UD%S>}xPJKppkc#iX|#{Ij> z^q!cYYx&o@&NG5f7{~C`_Y2k0Lz;tXTevQ#eb4Wj9;lG(adBUJgwB;3=X@ph7#6TUNh$9D3kx_CpAeofAwIH(K_ zv_IUTwQ4|8m6k_^l91o1;k>i{HNl=HqqtVj3*lUgVKR2P%TqYl{|Pk6aAnR2BpzRq zn|uWtWDm9$;k!m>)MNsOiO`uio&TK+TK=Rwtxzs0H}i^GRpG5yekes$4hrE5yfYT# zHmA8Suuy!}>qf=Zx3x-SZs1pMg^pKR-Bgc;1{d8|n@=tPE+_=soMkdJA(HLdkjL~f z)2Cj;cI&aj7PT(sOExM)e7tBA*1e1{oIU0&SGg zt*?eU&Zxr%{$!c!2wG4gb?o2cB;#K(Vi0RnvD-jRzHZe06Cv}eCLtcI@#yegBxQ*X zGgl2|qA!4y0!pEsZ}A1%B(VNOBWR1N!S)|rb&0KO!{Z^gxmhjP<=}r2LEyZxLAk=h zXd3x^4<^&lNouF{A}`jj5@W{1n@%HJeDepJ@~QVS$?7Jv+L zGAeR(=_DH3igR|dP4R{X)It_T#ELtyG|3aK><#ktn*bH0OKQe2$$-t~$sjb-9Dr@A zk<3QFnR0>tt>HOHKf=`Yx6=Sipa=Z-4f%L z87rV53N<{a;gr0TnUR_j;DpwZ1iHiq!xMER^35D3h4f`I7$*+$w5FRKvE}CcsfT^} z;;Ff6&GfZY3Kwxe`0`kC*uX$fRwt2jG2NVT5A7cxH3IMVxwWUH(v`-H^V989tfD^H)3Mp~5NMyw8L}Qw!iD1CM7(U?c3n&0I_f+nM zaLuhQeEq%_vJm#LGvvS}%<(v>6ZUvF&Xy1)NT<-B6wbmc*li$3Hq5I+1mwt;!?=4P z50y+fva)$YnhJ5zbS@wD9CJMdrT7gL%mlpZ@YhMfgLZIIXBpkX(P}KTu5^S_{7@`- zL2;`>@pN?vxL~~DR9t$9a)AmU2?A6KIIyZp_7in)u@3q$e{m3MxaMQk!?72 zNQH~z@|QW2nh2K3VlQB33h3HskVzfeiGScoV}fN7(~`m(fp%_QNRe5BB)_qHYiVt=eb1YVJyiI|06{rPaatb zst72N(IK3|Q@3iA_4N^Bs?>(4(_SC_O_9wzq}p+**Dtz`n%y=VZ()jLz-IW z<7f{{6@3=oSizC+tMyx-&z^bg+t|Uz?|%BI{udaug-{?LmCQ%6<9A-@#?Z|WmB_R! zb7(=0l+={58kytyIi4N=^UN?1ONQs8_qnHyGe;Xz!P0)D89l!;-mFtb*MXsOY-RF( zI57%%NDBGE{f@;xursoBOE%w}dGWob7dpl8X_b_ShbUgu zO{|f{ArjjK7&wJ&M}-`%t?Rs^Q&1=`B4M*F4$RyH-zGqY7t6W z6w5Xpztv7z8(_dL?iCEoN`FzL+Gt#ts+z=&-D*OQ+b>giNisX6{&Fn#SK+9%4VADA zx~v4}+(&=Om)WYS7JJTZw`ppJT+)mx@G3~?G%@c`ldAd zt3+e2AOD5^&?+zWCB>R)0e%ibRaqjm?BZV-qNV13{@_x)gu=r$X(aw~jLZkkBR{)k z3y#x&ZSrie#-W+wEt_M%&dntEk}6ohK$}^xP;B%yXg4ZFq!=7!P^lQZku?pFaUf}4 z$3-&oh-QWefL`kn>Dx$gyZ5B@#}>HnQpHw4%N*gz>;#y#Aw;O~{;JbgVSf{A1u5yl z_y_ZI%k^K(&veND!TjuPv{UVX1QfNFq+2uQVqV9`9NzgB;1jb+OwPOKou1=MADpH!1$y%{0NLi-$GRkz*Fj1eJozVX7E>&(Dz&pNH`9dF-`dzNP#g24cEK}^ zcgsj+rvmqXBPm@f>N6uQyvz$+dVGL9SJXVuvv}+LU%lm5hge$#$haJt5@)TUX%(M+ z(q)w=Fpi)$^m?_gS{FAz*h|KV6PFfvND#$7$~3qoR2;q=6E4FZMte zjn^g2rd_^(QwSESFfu_Bs%-l57B9(GjG7*wfI>4uRt1K)Uz8QLz*Z$Xqr|19&cwBw zkQ@nuLeCVW(z7+dN}S#HDrJnZAS;@o{@Rj~R_MXpZQTDav2;6PpX_Jo_Iha{Z)jrU zc?VDO)WOo(ma@x`=c4y?;zC(JRzN(7-7nI$-`2*Fo`NQ&S*E1gt`at*{*F5tRoY>U*iQk0tqUcjvrn=;G4ACIj<-I%DXE%BJ)*W?CN9 z6hE-u1@BD0l(TO@d@n{iyrB>1gb>_rMh@eO7icjI0Gm+FADd8)0JchEZw;zHd*?9W z-^duvb7V{>{J6SAI|81L3qK052`$k@klA*9s$O}q;7Gb+Mm*K)(gwxFrK*?HnNl)T zz^Wl*B7sdHXC%P_rx+E%UhKC1MGT9#`RX=`5go5m5<3lnUcY6KtvcsM-pI}MpE{xO zKpPm(EBT)OUz?OmJUeVGo%893XHLpy?#b?X2i-q@o z;S>M_{T|9H3PN4NCP)qq@RtQfx3h~R2Fuo)cOC;_535ZvW48r^{X$L|dUmd@FUyf*ke8NYaq#IzWqRHvG&2G<4;eoV%33kl@~Z?)DBW)zTNuaM0;8N;K3W^cpx=pao;%Fl zXL1vF5c&}tejO7L?i`l?e&Euu-ulY5%I5R#wX{IM^eAaGv<6j~>r6(-W&Xhm13BE+9 zX~F723t_wx;aD>n-~_I8I8IM2ST|P&=D**zd-CbcYz~mm^UM%WWXItzypg`I^E~-_ zU;qPaq4n`a1(Wy6gIM$YXF=Y5(yPJZqiMqc)%T?f0`?YPJs0w)Z}d8BmMh+iH4Bsd z;t5-;zN%uGTl?Y_f4>)!0E$s?l|PL)KOW^n8O5O zOH!TJm7hyf-WpIGoAA0JeeiMdsD}wQ@h!^=nNXhBD#88jTHC2?ie^{D6zg@HZ~b10 zoKOMBb+hdoNneLq7Sz#AEzc~?M%MD$@XVAGHu~4~V&aRLZ(OcqjxoYMmorz3(-&m* zmG=B-QUR=lZ+{(tBNS2CIs!GrOK~Iq-@%_0F2(%PO^kgvSIJB1C`_#*cBWOjBxJLz zsqJ^KF{E%PELPvJ3H=trP_zta8E|^$f?24<*@0{nvA|*? zv3i$e&B)q9n{}fU&rucI(A9=84ewMBA8o5ew-dW>h+Eo^K4M4TGa|sSN6cKz+yrSld)GS8rPkjqbB2ysxv?JN8^a_U!)vCftp>vO82McoqX?x=dDK=_ z88z#6z`dFF(R32#=lrswiQwL9k)8bbxXn zLnpl{Lv=o#0v2fxh^i&;JxZDE%MI8GP?XTML$CxC=W!{;Lk+cxCVxdN_R@2?n4Zk+0<(+2w!&B6V zDu2YFo9&`(NSq6X9`!ibkJZHGH%R@scks^Xl|pHxR0?{B z>}=ItPNbBQoQ%{)LYaU}KvWgeTE}`Xvnx^V&0Q0xp)Xvh8I@js3?Gg>!YT`h^wp$5lk$j8;0D5YyeW2V7k z^d_zaIiE*1(3e*sdY_W#s}&3YGCZ5ptlP4)@085y<*cytP1jAkiMFSUv|^l}61yy9Ew5`Qp;^Np9CR|Jrstqh(9vpI)K32meq# z@d@0gQiEGxQN4eFH|aaF*Yn@n_!8a%fY3eJvsdU0xg{7UIiZWX?Mx4*kGhYV?l-OG zmm9S9+eW|N9b*4q(9hHE-iyZeha;Erd1@B$VH7nGAQLN1{;Bn+G*UrsA4hWjf^1}Ys46tG0oCsA8a#Hf@Mnp1#6fndj={z$UxZ> z+yHHxxxQ{PyakFpp@LFmP^8V6#iqnTHFH@Row&fVz0*&dkKoaVT+c&FWT;kE*?&8icKW-$|N5bU462SUv9uuBwG1 zNN6;PIvxKluFHG0z*_G_PZ z0CF7sjKW-Wdbpp2m-jL&(x48{bq> zt3M-2%Kn2W`F*}FXZxUe?{d7vBc3>mw*Xf{8A&M|n2V>L(j*@m0f0MzZYDgfjDcH2 zy;hYQPdqWhwexQLA%zmqiU8zLxyUv5HjThj>uJ1T{V?(GLM8P`mFQNn7=}Py>K-L? zPF&9{XMN@V)9+BR)M}?uPIKzQPz7*6*$5e1O}q8s`~B6?*yowMXeD@Kwgd)U^%khn zUSL{61w#}$2#c}IN;)P2cDdx+F%;d)IOI5FY?WH4i~S|~dEKRQAxcnmkq#Xts7vqL zst%1BW+|Zap#f$3tMPW~Tr)|j5_XuhX=235Dp)U!B+EIQsZl6(!!X3ecNY%~Rwu`d zPM`rxgs5(DR6SX+YI!U#I1zJ*DZh4T8>FdeL5PKjUpq!g;;9KESoZC@igg;8T*Es0 zt-Ywjx`S`?_qqTe_3@9Tt6M-`-u{6K+pqoy8k*OZ??~-0ld*_{lpO*(gDPKBaxrSV zi08S3*Ug89BF)^~?~jDB>dB+g#*d(RSIQCa>#b#326GKk|UW+f$RB;>o!x$9$@*?9QBh_#j=ZWr1 z{IZw;z$VThH0Nx|$dfEi=0~m4t>yEju3)18mJk<_=##_*eNk3MPi53Lu2{y#Zi016 zxq(J3g%X957!1P-86!r}(e6>lOA_o7qY5isTv+TBQ&IA09DX_k&gZj0343Y~aRfu8 z&g-z)%KSXW=W<=C()Lwb>;2_s&QjZw9+0)sqWcKSMedDefYK4uy08?)^<-Cit1M|v z8o?kXBMv%tV@s^YNcQpj$2*6g@DE~v8u!kJbCg_U(3+yH+LZjLX#R*3UG9ju8mKBi zhCyv)PDDSe>1{P>nU>sNo0qVk=S^QHiO*`Ma8a2Pon+#OMo4^4;=6(1MotpotPUD` zZ`vQqu$SE4&8(Mvlpb?S8n3_nSepW{QvobkOm$7AhNGi!R6e5#Ua+-j9UUeWp^XLG zj}(aU7&~dy=R4uqbZtA%dKt;F4NE*t9}DL{ybyNaM_+@8MAt`C&6ha!Mirfg3oK#S zOfw{Qe@sI2+FHQB;jorP?sASltRbDvnEa6V5ClU<9asyFjc~BD8tUE<*<>U`V^8RkPY4WAVYwJQFZvAH_bRr}!wpW< z@Q4ct$#<|toIC4M7~9Mps%%nHREi6f4vgF=D88v}I-_3al=Me@)|CzJr@Ob438Ss9 z)Z)N^fVrZeVS40u3YY};c3KLPR&;-(V#V}3k4>Wkm?a*cZbyP5sl1q<1z_tzQP<7M zWEu*`vx>mvt3`%+ZRJQlP*F*AC}p*QuA+i68)#jsdQ$WdABH)> zqg0U`d52bq9kYC$P~Z<{RKsGSuJW&ug#&27sEY?pFJt=h=& zkZ|~@`m4eB)wk;khYKiD{{iy@P<+K3qldNH#~oN#Sx)fAfY*AwKv@)*N8+UMfdv2B{NqbA?jE+t4l9VXV)XqYa$o9fEAUe4}efb~z zfTFCHA3fYH9C@nW(@P_o{`<&}gKy^OVY{MihkaZwy!p0IQ4|TY{3{?P#-%#aMy@&v zsC8WBdP2qUUN+0@dXO~JRh16sK27p`+rt4~jvYd=^8jcx`Ste)u;9lx@ zX5iNw)yK~iR=Hcd1(PO;P4dI6R;j=_Jag0BBqelSZiq|v18ryEFX`|SDi$#mJS#r) z_Fd_kS`OajJ3uP*?;=0^_zyh4Pj*Jz56Rjkm`h8y7sga(`}mYGcCkIEOK2(29KzX# z#s7Sl7J|gmI|$pqYp7`4%WCP!xIsNP9w1E-LK~z3;(F;z}x{ z37k5#*87{ z`nylfP@bZm`T2Te!G6vAIJ_wifujkBG#2_IRbDf)MqAGzwa zmCvn0{L6gy%7lFXOX%n9aI^Y8^FIG;(=HqZ5>OG#8)~G)e1oJl3Ke+f2b}7J`BDyT zai)I{g295H#6poli|eBtuEA!tWyr2Wg?NN`z#~<2)`MIwPd8T5kKROiz*AH^>2@(L z0}(NReEi8l6rQ1OVzhk>>`L5e6Y|g+z+Z}6mbkMjRG}(>yGbd_PN*nJKn_5{V>KR0 zdGlyd*xWwd-r|&ed}-0HCyI6OZ&N?@`GX6^RgZdf9t}AE4{LWF6;%W1dtZwh`|CTC_x!z1-)c**D+5XkxS6 z=vbEcWi!$=72RIHHF0lNJT||&X8o$+Som_RPjGjVr4J$drDrPwTS~@eSAk!%+2_9Ia(DisXW&xBl6C9EKwVJp zrAK+T3F3#;skapEanLmTcR6P3p!zIdMc@Q#G zOJ=&2BDF0aW&Z2p`-bNB-L0>?u*#Ailb|WtSJ}7q`WsU)7c=k{{Fz7{8@%QkT!|;X zjg#hGH+GAN&jP23DxRRXw_typZoHN=%XDymBip}lJ&weC;pJLlf8gpwW)S7UeR*Cz z`G$my)7C;E1x&Eyd7)<=qfdwmfIwHjL7+5DpV;*Uc$MsUK?=^=s_9*qlL5z_pG8hW z@eX!rLIh9p7oZ-{S98S_zyqoTJRp?v!LzZr-yRUUEYt(isW-6RRQBK}82Z}-S_3>F z%)rYjMYsEI>tYi>3KD)f&J4K0`AbnfKk-dH)IRA|{R0>Sa)TR5A_ynLx@8vFso=WL zY(TJGX{%40?XUu2Ah`ct(1Il&V?0k(Tx{nC($0UM)qKx)KCG;?(Xktaw^vdNiyxhJ zl(2mFu>Cnjp@D9HKz^u#&z@xMZ_u`OMr^%hxThuTPY#*y!IlbF&ue-%|Mmo=r&3(I z%(=~9yQUrt`|L^y?eqE*$jomNXf$J$?|}p&pAt9tn*@sbiv()=Ljp;}3E;*0N`=mz zXwCZGrxC96rg%a64Lr>%hLKlC8-`f072Zw@0{?%WFEyA@jU-?2f* zKT>#(?;Mk>w(p)k+Gqtyk=!IJk~xb|$p{{f@X9YoOASIvpt`?Epy2;N0TY zGV(Kj{ojH>(!XGTrgVkF;5~{iN3%B_UmO_Bm&6N;Bzklz_kRb0=u?Scnlc*Pgv=gU z;q^btNr0&kd_KFy(^wz-Ypy>_rcTO{=_~f3+w-s*(n(}wU611LW#cpoBnI?M>zgwF zEP(WWFM#mZk=)t0)4Vh2oXB=mbE#Q#i%}u&ZU73zC0nB7{dEICfvlk@P=6;pQamX} zQwVyE(%Xpj$E8gPko=XkOA-5Y^Jr&%4kjCsw8U&_>AaPIfR%MzJmgf-jW1dr(Cv?- z=pgSS1`1eCsyg}gwJG^Z=hOtNf{0-GEZOnXH@V`e&vS;ou{bS|)3^u?`49z~d82gD z@%^+j?h*vO9s}>YauRc%Lf1FgRt-`E)p7*8KMd>2@AO3}&91h#%lzdr5mN(3x>Dv$ zS{`^HPsb5Bd5BYaItQVeK*czIXG)Skb2MAwgg&$6z^PsC=alD$!=!9mjD4IC0&D^` z2t~YmwV!@+G`a9pR>?DICTqV$jZDFfCULkpX4MKft921oJxy78eFp}0RvB2tXB1lI z>EfKSTkpOB%8WfKVUAgZob@nwqZagPa+KxAsWupDo_Xke$ zERe^ekKa*N-PtQ0#zbdSPTeRC;L^8J#`!^=1dP98#UjPBw?uNc z$42*ad?iV^*<#WYZJCt;UdW`MPD`(LhEf8DkDfA%w{FM}s_G|ZjC3V{T@Mh7@e=l4 zw%Yu5OrTo}VAm$p`n-^PV&4ptFhqNS(tE}?xN&yqPi z@}wUo0zsRHL6&eez0T=-bj@dTZWPB4P^cU()V36#`s*BfCak{F#d> zSKslo^0Q~&5jTsfDaGk&J&yhOK@Ab;jAoqaG_Qt}`MDY9F_)I1`^XMP|hmcNDE{PcAy-215z$=kuq_cZ&8wd551h4m_r zm9k(pdNLF;e7zt>Kt3-0^%Jr&9T@muJ+ z>waSe9fLyms_{1qCPObMUywU1z8!KV0uQv}MX`uwEO{s7)FOPj9K4Nwzwnxx)HNB= zb!02&K7{KH-#naDuilnW9WGHNr+i>x-bR%{wR%v@2{~vx!|ELzWr)ldE&)2?&YuK& zAXj>WCeBat76t2y8#tD3m~p2E8PLTYgw)@ZRJv$4nKn4TXaNl7fRotP&$G5vr2P_y zDWmH1BzD7IP&}?cGg5}p5wy+M567DhN*xTF$B!+DuuDtPx)yjQgR;37fK_v=N0V#% zL|IjZ{6@lq8dkce6#g7GHI7DhE(t56t{%w)8<+x7Yio@ML%?_i>H!=El0n3b)fY&O zoQ3AA_Ew(*JnXNz74jfICJX)>{YSzUMMc#J(0?Qo*An3R))y8^RC^X=>5cHGToxK% zKDcKzsl-c)A<@&A0?evm@(BwS37DO{T~xraZm%PO5=&Gk8aMr9r4B=YvZ`kM;4&d*6Vrq(wOfii0fv9_cv<$(?AZaY%#{m|J zBx?H@kj4%ns7gftHcqX{Vp=R0d-xS-+F~3Q;vS?Qql2jb2UUm zs3kOc&2xklm2AY3hu1lx38yan0{eB8xfhsmE~!7BsUYXfLe zoR@zPAr1f$y0Y!$tGaPOeTPiN_;JJYFj$O{S63&Nk?vztJcFU=!X?gMh|r7S@mzg+ zE8dw1`nDsx%%DVYr>tz+aQEO%>tJyFw8#OBUDmMXCdFgH@219oBSKC>lkeGL9=}fW za*<~!+(0pCf63Oul4P#Anxk|t?=1{xZOqRGE2VliQivz}sTnD4zUac%Bc8E~;Q3fq zWKPBuDi9#5&c^afdh`Mk%Z~a&!^gWlcvCk9ya`LSK~Ezm`>2=z$=uIbAv_jadDBjPWgiyx2e)}O1{J|ISh9}<%~j_pIozCE_0{a ztw0f!yb;MZZDxYe=F5s(q~Psg8h!`lYF0}7xWJK?e(pxz68g)%C|zEoK|@ycoByyT z+Qrv4U0fuH0^1*e)J~Ao7cMj-JGXb(5~@*ODbq+UPs=Flz?Kw{3^P`!Zl?u8aAg!C zhP%|=!B~XbaWp=X?THD2GP+OziR%lSHsuNmPpy~G=yuMmyiCf|Ekf92Ss5CS622kXl!|5!{pP4=QMw#kYAEujv;GKpXvJAMXc zMd9sqym2cZn||_?q^5;Tq;!gT*^qEaq05f+=9Le76N8UTjxHT8+lobhSx>c?QhDf( zvb<2Z#lbIO+1lWsi^Pp1h=uFpWu?Rq*p3LBKtiX5#os%tCa~hN`ojoCSWXD^7Q^PJ z=UzWaH#s3ToM0Tl8>qFwV#XuG_hbneXLvQ;upJU1(A=UwjE-MC8gPGS=6py!dcJYZ z^Rd1*-wQIUZp{LMhkB{8~^?rQT<4WX< z47!$1H!|s4x#LFR=#LW)FuH?bD)(-}*XIhDbEQ`!8{17gg|-^?{fbm`Cs;(Dv2V8u zCiIDw88<0>e)cRs(XuO?w_BV^qON+~YRxwy+u`6eqdVBz*=3GT*QYOkX~l4!C|T<` zeY)hPtQt^56loscC}e_Q{_qh40!PBg&p?B3jT0T3WyfwFGlY96la)EoQD)e{dlBp- zR`v}sxuWlRhv;4Q zy%Zrw_sZjYyJKb{^eV=-iEqufGRzO$M-b-1Kp%i}BA5El~F zr?7vzJl6(?Fx(l7SU&9aZQZIxt%bMEI{QrWuSwB#=}g>q>xPPzbLEoe!Ss9%OGnmD zV(nq0u$|tnUOGc*c9x9J_;=2pQ_{PH;5U(j zm1GtRdZFJ73AO4Vh_VT5D2Z*C*bJ+q+_h;hm&77)Y2Zn>-K+wmH6Sk&);!TxeztDF zF89dwbe1@cdd*;Q`Dk+75bNpaYBKTZ(A4v{WE~_vSMy5;#f1=IYP|(h!bn&nthBF- zZKt)Q0s(K?4JfE(L4Dl(Z{7xFJawQE|3Zf;4qL0zIn*L-Hr*Oo6eUzXr-(b14Ol{@ zL#&?p+JiB3A;~lCyw4P&me3s361uFb^>y>Q+P6P6G(G^&EHn|&`l&UHTDNJo2|bCT z4XRKIAONYZbRh1PVHWm~X}Y+M3A^}w^QGd15Gxez@vzzFv9E}4KBj@uB8;X9Tq-t{ zM=FR6S(C)I$1Fdl(BV!_?jb%`-l6eyK*!FoT+N8ruQlW$Y!l^EuwQ%6-N8uBM4D;8 z)%|m0d#kZ$EioB7U5Kgo^8EgaBSb3L7HBL&4bP2X1j~8?Bn+KPk1(f0?uaEW*K6*&M4qz?m;W+67uT?)qBcWt! z!ce{x#B(bIK=iDVphAPBlAz4rzBk+QrQ6?=BDu|`h049t?5t@!G#FG- zL;rFjGUXC~3E+Yg`Ek^Vght#d0oVg1Qb_*~NZWVzCyL0~v-Ad4ah7aG$%vA1i(&zgIa#$!->{ z7Of6|*W4HxYq@*>V_)X{1$0joRh^gU9EV89u%3IEudB22LDL)}{G;$ul1_zyTkuwb z89)JHKVJzp!G-xu|CwG|d-LeUORo7J3%v(s>Ds~J0mmbHGIyDKIvij-GakI=x;X3# zd*?dX?q(icm{Ldr01;6is;nm9%0M9`@Q~&?sSnA`{Dr&Yed2FPh!K#46m{7|EuRXY zY5Ab{jWA4X?R=vA*&7(C8cP&mm;^BQTV>rQ5p^36l2Fva!I%wyMZ<#N9%iHRm2A`^ zr5`^j9wrgGMCHc~4PvLFv#y=O6}PM;5NckkJ17M`xZsjAF@7>pz=kDy%XL z!p$^m&2u3B0|@~)K^{j8PW=lK0@qwjzA#<=XHmo}Wa}f;e%0>&%V{^MY15AU@RlTW z^IJWfb-f+YtxKbWQ?r0JQ=8fQS=VK{E%}7Qb~S7Yp@8^=_;0w`3Ih*|q72_}O?;Ji z%DN=`*vU5|VRE5zvSVP3IdscTa6;;u@D%DYdMLx6P0!4nZR7P6u3m=^B@iI{tq47a zDnd>xSTEg}VIIC}!uQHq$MpmkTd(O2>CFF*}D*Th^mtGXDht)nt^7@uyFKi z99?=e8jsxHa}$3CyKnbGlV&+z<(wIlksLG5%AN!__9IF?5JvMi4!bA1o8R;11wjvU zB5<-k_b5F#?(aE~JgdxKg|WOqMGx z{lKv)YqfY$4HajX7GMY!U=mxz?U~$KxUWvWe4hFZJJyf_tKr1vVw`O4^s^xQ7zkCTJgg7f@xTghdkRYBXZ*rRZGja2*j2<=cCKr zCtXOAEZ2+-o}br{@E1jp63`IW5U`^o#6A$eM$c4g+b~?e!jtO-jfc9rXlvZm5MxP) z%2cyLii<~D0*mWyPL6`gUqM9Q!^P>q#dVnu*t1o*bTiO*f3i6$b8!% z)R?~8FHW7HE^2zLI3SLdIt+}7ewvCBQj1g54w!ltcU;Zh->D0BEQ8n~dK?gno{deD zS>InEW;X8Q_!YRO94;SV!J`8%+Ogjhnp+=T7`hG7tysQ(Oo#VsDU6+x{qVCg-s{i{ zWv|=M+Jg^$vGe-d{r%D8>6z$d<}mf+1~2dB0?NqAd4$+OOWI>cBP~VsRo(ugzve_N zURdD?&_#i?vRQxSTts?$@s@SSpo^nY>hE?i)PYl@=vy;N;;LmqAEZ(4;c~(mT2F|%BOk1m(`g?cm+YsS!WCYK!2c3m zyo60YLCR*R0^b%HK*OHgRV(Do7BM71O zLj@t7R5R%ZK`5aGD<)s}W)WAC@1X{DiG=^EsbQCi3gBK4g2Xk`DmvdFS`g4y!lpuh zgNzyvjxcIUuty|pG#mne(8YUyIGtIn+8jt@@YU3mrrjGvqK$@VHZIOK#1+$j@j-3? z9|S?y27T&`c|O=X2k=4azxkk_LJcTLT1Z+kD}%pAsyJpWUG{sP71kI6T`^5}3`6FJ zuGp6SdhRA$2m@)Qc9DrgFLF2Ta>2)7SA$$Eif&xaCe_R4vz?v~#7p=m9;Ftj@)kY|Shj#1mMu%o$0$XETWqGFx}7BBKj*2pH! z-)aXg$D&A{-*(4^$3Hby zCb#6o;w?^bbVO-nPEvz&2+vldHmY>f5PA0^_yv4KK}`+Hv-9dRw=%$*k(62}|94jm zcp;Zi%Kp16mRbp|`fUW$XOAgvP z^ssAlGrK-%9OGY@4;9KqNj=<7FUdP7>k!`Gah#~}FLB|Y_VZELN){VzL31lpO z5ksbf!{RggS61xay@zF#tK-}AO4~3Uj3gLH`M>2LOGd|YZ(8o!SxfWS@RZ&$#>$J~ zwa|wt+`+H;$^IXAlgU0Evcm!6-W}C1pMtfEcxPG73ExXp)>b=mM8tm_7OfY7L2Ot4 znvxn590RTb;gw#sAWpq%5iWZpiWj+c-COY+4jRlEhr&Tl1BP9*paF4iF3tq#hzJ!J z5vf0nh<=Tc`R?79R|$2l16eWAV%)66hpbro&3B{B6e+RT;(1YC5eXDhWcu*Ik9TYEyVktPVTLrn&ol=zF5JlCuQU*&%pVQ_r05fdlEv4h-;v#?5<;c zVAt{*%|HA+g+-u5TDA%d9Y}_h5B2)<>GCgL%jN{Iwlxe0G03tNcjG|cfe-38H*j#* zk+{8r5v*T*kG{d_aMr;p#waFVO+auaX6SHfZyB*m|4e6E6IYf)$*AW4&F`I9m>OPg z@n<|n4vDb3ySY5m)iazyWeUZvZn}m2kh|NorQNK(DvV;ouWBeZe~pL;(V2h|(U1Qy zB2tj54^}EEtRd^hWo$BTpzpUm&%K*V^X|rV3>w1zT@@SF0REiaE=_TSR>i{pY=|PV ztBr9nQ87`mj7tSbGoNRDI7dvDiVKA|A(u9V4}dc^R?&1cqzuvq&c&94z~f=3HyRIg zPLnCnd5xLD+%$Prri`GoyVT6j;7<6 z{~i%R9DxxLWWfwI`!#B~I9?te<5y$57U5BJuG#)#p9p~d(H;=NtAAu|jR}c9NI=O? z0^(q)jCnw0>|b!u>OXMM<Xz1Qk|-mOOU*b5nx~EB6qw>) zzVZ4>Niy{uvAU90pq`c7n!;K{l<8Z@`Y*LxajpjL4j}vFykx=0kRNRId9l0DTvi7^ z;p_1s6b>@^uUWCRzq4Yc72FL`pZgp(k9UF34&@9?bR`308_hM% zitu{1O(Ug|>0O_OVt!of52YnbE`fhiU!KwUqfKi1n@{Tx?0Q2+GWv#jN^*D7kQcUu zPx?lfvhM7HxA7{C0i_66<;4@lqbWQ^q1(?)+D&HXYm>kFSl%`^yZQnO4jWZt^;_|7 zLYstGkx|F{r;?Qo8;DFO&+4>SDRXt)WXecSad$L49hY2Sg*MwHk+Xthz0pI2K)42s4Cn`MC(zBPpR@vN!G3u1m@mg48N-CzP$QLG!8*cn+Z1@Uf#R@0@J4hC42jK#C z(A+W>v=0WK8M52fPvi?+QU&kXo%}@yJ(0|6f-Nk<`&U$~o2Jr_0(i9n;86LmilJdH zXYRen(5M(>u%S2Oby+H!<1Sg`107`Q)FMAEQWf<#9Yk&cjf(aD6BTnWpK?iS8yY!l z$g9ImAa$T*u#q5thgF>f12=UKgl^Gq#baayQ)h3{{uvR`8G#L_aoSA@K}RS@C|Cx~ z7ZV3wmrXO*ucvoEGa$JKFQ6H$;yLx!d^8ftUVX7qj`f&<%%lFt%f@kE-G>p;){ZoGP%|sn^n9HK)I4hU97qTZ7{wp@E=n>tb8ss zud1Ez_{xft!D7T2M+V`h3}Ayqbe>4ohB@4u<|uEq$Zn@-BDfvr2wqnDn%%k%sw|fj zquq^kZPP!+q0()nE#O9MZ8Y;>~5dunFDkiJU&Z3&#dXjLDVVP16^Y<~IO zmg(}R!TlMTZFvSO5=ygQg5XiF_tBDLe5sm-*WE2GzN2RZUrO7mdt@M#Q)%gV;|bKuS@M z%?#bN+kBt{qD=2Q8mEf493oOgS~)DwD=&osrIh(Mue)sge*A zjq*S5>6m; z++9Wsq|L{o6>Z=CSiYCJ4x0enAiB$(q1MVI)JJ3RWp&Bi?pmg72v0hW z3I1k-qRap`s4DJkgz@_+zy_5*jTIYq31Q#%;Xj)zoNt!v2pMtNys2{(e$&m^wX)QM zSXy4IN^mwHq~RX%B3?65g8_wvMpesyWeiV7<}TD(qR{BaVe$04CSwz3BPyIOm_VWhooA?Y&VM6X>=th^O}NT7S#K&;0B!kixZgK6eCT#f9z zqWI;xb~mmx>D`n{(feyX9wlSxPoHW|kBJ>^o_|+floL#1r&2d@uv&w&|ytWX`+~bP(7OA_}F0kj)IG@2(1Q`(yT=T#OUi`oaICN=M|cv2-*&>G`uC z(u3}Ydi(oYDqN%9Q)%xp&kzR&AJx6yqL4!=b({e=XAB>CLH;R<3Cx=R9uQ6SXF;r@ zH*{%DAS02NcB$O43N7O|g}3g$Bc{@RUI2J~k9^^p?9iQ&~Za)_bplD$UYE5aF;|CK3EF#_ie8WtOp&l1a{!_N5P zR3J}L9vUQ{m7KS;AR5Ll_agNf7H2WAA>!_Ie(H8`=|FeMeZL6^LX+8XA$E@{fPgmU z_ZbV_aIXfbN2@^}E76*hdb8!E3*?t?ytrfN*2vYW2_2b42n5mT)E>L8`q8<|{@POS z!;zHWC^BP}>PwI<_VVY(WWUv;Wiv-6P{TA)G?9-dHi4CE_|n?t2TX`+n4MTwWH7q@ zy^Ju_vV%IVTj zU-!yO$h>(SL`&Bkfe$_tK<)gOAB6nkIOv5Vk@N?Fzcxf0|89&e{%(vRL~YnI|Ft5L z&uxu-SP^+;neh=VcEiamX8}DM*r)=y!BP@Br*-MnJuEobTGDwmx!cAX`4arLo>0nV zG70v)0m2V$!COZJI~QJ*3y}CWq_Ky>m=fV$kMyRhb%Z3(#{y#W-B?JBiV+FTg_odvA_W2&V~~+)O*tjo#E5D&)22Rv2a+rv z06ltwab*%j3GWaj<0)Ry1>?}MTRkO>4>XTHl8K8Gkrh<(*~d%$;vsUh?+1-~!O6?G z$Eo$o42(gbjL_P)49ZHBT2Q)loU~(-z(ZlosdOHp=xunQtD<`xV-`q4x9aHT7?i4t zO5b>NkSZPv&XJZFG%aFJC6lGk6iDP<^M|L87pTg4qD6!g{3(nkj0UKlsiwm9gRsn0 z{a{&7emBPKXd(5gURh7<3ye{n9w4D!i^2Z$EUr1=@4~p}u_T{|0$FDDrcW$uXr1gn zS_UB^6zJ&d2{5HVTCU#*kfbedXbe`&iM#HIVwrVWU_Pxr9U0acpRhMFYj;as>%V=~ z{*^IW1CLIJo@sk5IYZAxO&3yNlx#ja63EWWaTJ4`vq7RE_h^?~vjMJa$(#n<#|UGR zg<*1MKD^tfvsuJWHgH;ZkqLwT!kvGesrnok6K%cJuz8+@^{lj4u96mHjOX=~qV!k8 zC!`->x9<})KgM!Zr2_hLUj}HpFv}$7ycJM$wAwD!BdX6KdNx)m!=nS6;dKZ}pn5l~ zl>QJI&)BYbYBvQoIL*z=uzWAztGn@T-B02%V+L+vq;z`S zfVPTAIP%28hP?SB7SX`rwWEetIl6c&>F0}BO49#_Q1@`GUW?J; z(}6AGj@auY8HW-?Mbnn7u51Sz)QS>|C!dH>Dg{VC^<+zSBw}6@^n39g$7zUU$jVd~ zvA>Z*@lZ(iiw^8}ukFj|#A{RPtj6SP^m^Tnb6`#6Q->VYs%QS!nh30P?E1SXrh^gZ zG|DLk8@tG3+Y?a(gBA@$0u_5vlO@MEI3^ruB6ZMYfTav91k100GDiL}!{#l3Ezx0Win(_d z8L%Z%CE{&=A6?+O3rb)VlD(VHdvTFoCl43%P7Uz@o*g_kv@8`6-+&`x2d!_BOl#D! zG=C0AkhSmIUfVbE@0DsQOtry%EZ|5h{cgkK`W))8rA)wXgChX~ z7GCj13jxHCP-MJRmek~b!$QED695bG^h`LW>lJV1%E%^9=zm>sSU?e!0`^4UGXS@K z%%#u`Sz8A!Q0lNNBn`7jP3Y2p(gBJ8$atuUjXa?MQEb@j^b$P+LMab1F}ZP#;@>eb zoQIg0BiA-!Tpx&>2CqQ&ic7J30Lu`xDQ#XFcRBU9Ep$S^)_|6hV2UXILgodp7-pk% z?SPJaTEY78uvi=r6I1^5W=s2Zew~53{X)kpEU6?vD*n&0gd|qSYT#6JyikyZP{Zok zH@~+Am+N?F*pk}Dkn<(f5N#l9MX94|>_DRh#KcbMmJoKx4N?-i{ZjJ9n$;&9EAcfv z)|dH^8RYyD6F5_Y6EEF=!$OOXmlgw*N!h;yDpH#pxjoY7vxQ&h+vfO2@R+x=2m*+5 zs5jQj@cCrUS8q{ga zX^4cmrh~COg25U1|91xjquG8^H-ujCOMyxU2cpxezst1i&Ux*^cuvd9lfC!ZJ)GpH zruYg7G2J!VuF_yRjICHQQkCCU966`v;sgoN;A)E+s}m00@W%{G^+1z*0D&}0PvN*D zvoo*U@ZyMCB^=#;fULV0F%5+oa!edMYi)vTrVRqM7KoFH{ju6+qNHhtT-G^Zv!*Hn zHCqS4`}kRrQZ8fhcE052yH0ltvF8;QF7h?rF!#>2F_1t~2^j5VJ4i5NO|pzpdCccf5bLNvW;2Gs ztB0Hzk872ZQ)FFQ9iEIOQGxTSOq5mB+TIpRALdPpV_$n9N41s1#mYLpdGRr!_@ax&ms`I*(YnLom0ntF%~8z` zC&O*u$V(f#6IjHSmjy&lY@5Xui&{h@gF2I*>mo8n@e5hXAz7_wgdD6Vf1+YTIzC^@ zt~IY_tTjlL4*3G#7|r9>NgnYTcI)Du21_C?)t#5k&}Y&COSQ4D!a^O8z%n*n;w(P) z%@304K6XvGZp#L8;3h}bO=wcg_m0dA4LK0~rUTM)A<$lPx;Az{_824a$J_bxD(9l% zfCy7wG}~c`!nv~B#4|xvp|b4JwZyI?d%msr81@7DAAOa)Bj_f}CzPJ{*0$UwxDvlK zIT718uPzD+(d_W#ZeG11oRiJsBjT=~^tFH%#U6{1RHoPNNcox`L6X%h25LV0v``*bBWqXv`NnkwXA7| z9-2-w)Fgg{Qk}v;u{G}KHeszP7qz( zj*spxJ!I1)N}FsrP-JT66s0XO`r@ZKAyV&+y5N({{7N=8 z`ipa8?|ezhS@oCXXU8Fvdz@k%86ZIRsF{xTgfL<*jyu^|W`%k>`1UJF1|zU{UQt#(IKCjTS=ChpqIxH zp8v{o;riV1UfJhMHph4Mnf(aP=3C+}&j(#}H|{w#qa{#Q$&w$b7- zCGN~$qti3q-B`d8M_9cv$44iwY+?A&6hnJ;G;xzX3)*x^?M8eH_YwIcG6I*o<^Ua5*n6|jCJw?v3C*yJF(=nNcrkKHhXo~&((-ad4U`Y@#^<|(Q`jF-aN#%88{=;H=jzO5mj>+_9j(ALx~tzU;e(tU z>R?sl=_*|NXDw$4~J@5dtF#3&JYP#Xbh$;gMXn zT;P5}XP#qyL$4H5PGTh56JDt>#yd_7or`~>Vm@_;v&py76;+B{VS0B6$4g`W+DiZ~ zqV2MRJSu*9!2cX`jfzKxckboUT#(oAq?pY9BtqGKO3}qQ z+*(`*FA(aI*=dHOyoPzw6vPey z=OkS$eSc_{tF@_G2lz)KvlH}lcN3W*dbuX+3QUTwls}tPsSoLPUqn*3mt{(bT zp>hTg@XY(#K?V5dDcU_$*z$2&e&no2q`P_F4Mp7_WT9_+-h5><4V5qDu#*kzy2jWA zVZIj8$a3JCy0a5#Q>x8jrmd7=x=>GqzUPqn_3^H@_2*7y+VDY`bTx)dThF29m9Wr} z-XB(|N<)@@MAsGLp(ch5t%(I{$&&&#G3>uKtF`LAmrKPhpA=^+-W2_owDt}W1e^XXM zb`k@X%{a@)(|kQWy>UkjIIA*`vT(8#c}l_J)C2~__val5!8t9p5);{bS8I9e4xrbG{@#gZX{V!B#TzO-&yD|fc3Ms_mMk)7} z6%!a-fNHYgV`MvnQ`Yx2U^Kf;j9m8{{fmNR%gl1Ba`lV~0u3i{mb94u@vBWYly z<>K&a1fy09paDT5It??-0!m{E-2Dbj>C4bn*4cN(F;`|=w8T}viTpd!8*98^pWueSjv3)5o9+MGC<1= zwX}MARORjdiDFx`a zrGHqV%8NUr{9CiLr?X7CDBTIV)jCIH5jq;WSmf&4(QQ2TME|`W)WaaAB3g zpGovl(6I<=iPs|M`SRRcvR(96)C>6hd|A!?=+2kdCZ9X2sLC%aI2*)a$fL{JUugZw ziBZ`kJ~9);u52Ucja z5^kdePuMj$c2smYAXukBcmF8+O)gmqg}Dd;29}Oh18n2745OST6w{O*+MPFEDO=+)h-IZV`Bg7HIVH3u4JJ`hj9C~iw4c_ zm{|ESd8htEOl)|yPi)BKsg{8LNBRD>aBN^oDr16r*eVJZ4PvmgmuS3x?Ro8T-SUYS z2$O14s%yWM;N346H)>)h2`AQ4Z+3vE9QnH?7W_|3?6Tp? z0;_jf;`G-j2OCM^KB^#Ryy74VZzU)xD9PiD7RRLm<%YX{O6k7t0smDJTlD2$xSWq8 zUhbH|`5zC0uvB#fJ+3?oxliZE$AMg$q)K6R<=-hWU{?*K#5&l?J7-v>iJ#4}4mhH~ zUB*frt!D%%G--4A0Fc-SBLayUd!!Da9kv{bHK+DU zazv$IEB!=~Dm|N;|}Fd^bnP96%o^N#(E}K0*-)ZxT@!+S!Mc5 z6_Ty}RbrjYu(vTYj}D6qoSIVuq{Kc^LsMeq)RsKZlvrHA;1Je7DX~=%3005ZsF0QZ z)XXQFY|Y-{As{o?eL7Eh{U;^%PAs-%CMjM`J(6niCoWNmcdqcU+RcQSOa=f11*vIP zX{L{UYSEj?V<%_VfoLgXCY5&DzUayQOc(J*%@{Q)HBVgk{6$x@jZZYu2xqVI z>4FcN<-O66wZ5zjE-CTs?(WA&WTyu)5Uh(Z521`2H3X ztd<7Ds6Zv7icP2c-I?%!YuRY}cglQ+9GQvZ-JHg@tEwD@<*2gOm#(aiwvdniwIX)( zcSVfE;O8Ong4S;G)DZFidJF{O@V!p4Th8RYO$0Jxm&zZ3j2Ps1M$GadBgXJ2BlaN_ z*4jQC$cP>9&_G#G1-);x9l|XK^-*DZ%H|t*4N=lOChytV2XqdWy9TC`ZgM1Y1MwL2 zL3P>{r8>3upJzT@P*5s-0Wcsq-KhC<#P?#cds`u<^GdyAm!c6G^X%ZB9H5@XT{o*L z76BLf4)23EkRC$`C!?U0(+|L7{Gz&R=e6`E8?;h$o0hs} zM%SJq3Tc4usW$hH?mq7ydh2&wNs#!FRHY;tkh`bY#SC4GO@|4vCe9Z?WN4FvUuf&;>mauAwMUI; z);2|rboynyxt$cl?YPdh4*~^&q+<$v?A`5uS(PL^zGKqV)M&yP)56#gzEHj$ z1Fl%VD8Z$EjSu@ahN1H4*+;*x7&44;Semfr3afHSB3M-zRphIQ)XTcx%7rysu0P4P z2E=GJjjZ6>vS~3A6XTr{J{2FOXRAKW1wNO2OMF{w=JoLzM8TA_LOnbv!B3!Q4Ij)< zqe{Wr1xrms)0c4sq>`hj6w5YkNjvJ=lhKsFC=G7-oY7EN>`*@OO7d{6I2uptCQ7`P z&DY_zDaw~-UwO4DDq~b{q>m)Xc94yRGx*02S0b&WSw5>OzgOh_K|G~?N-Y1KPFnlT z@)K82+d5qKoVf3KDmGc!T33mu=QiMu=>)yoJv|%d+gC2f<>W##gOr%GQkKUoS;kG-;Ze`m##II_mY(}e z*2sh0F8<%QK&)A)n!_J|%4Hd8g4K9$UCUaNIaPwysZNQVu7CMWC<>!v^0?(o11Sx?f;M- zLh9%n{}xa(CX0c6OO*%?fXFeNGlplk?`2UW=^e{MXC4GutHz2VQvC z2*=w=xX)`|kH?&_x~o`Usxu{P@?tD0O07+Qmh-GX@Lrd|F{h(7_=!?cds3uZeBJ41 zg4CxNkJfKgBl&3!xRm75d)>VRDhVagvF%nM z#PCJ)E5J~q!Q}bd-x)7wdXK3e0tK;=(3`4V%f2W+rq@S%m}8gmWD~B%g>()EtBdz{ ziG+wN>|i_r!Dy-zSiSlKvnv($aAx#^5d2G8$hwXTN1g3HrH;zkjve2eW=!eps(3WQ ztLVOc2%1c?R+52_SS2SCcDQG0zp8ntbvd)bqK+i?&ZeiiRGfKBtW!!xd&%T$d_G*} zEciG9WXk&|7$0Yb?CP94LI9k-bR@Z>~+nE7k^C8Wi}BdN^M1!pHQcNcbL#} z=QWDlKVoY8mE06M2I>@@2g}2r_&$DAnp~l#xy+F(^gZrfn#Yee3vlQ>ahKv=++Et@?he7--5rV*Cuq^)E`=h+9lp@MXYcnrci%JiK6m67 zV`L>{<;hyfdge1H+acc}N2Zrz%d`3hMOI`el!Z?FIE_UBuzGOnnEY@DS@uZe&BRqa zk^w77K#F8uh#LgsN;>yz-=DF_Qvrs#x($|I^9Mn|i#Iu_s9?R$`VD+gJG20AUcM3Z z{oqbl6n!f3Zb)R!Ah#qh_I~7NneKi}{Y^06KtBfk=cJ#JUEMBPoM$n>!08w+7<(QI zbmknYls*DN`9vdLav_;xY)>xCg2)~j21>T? z?Ew~=?9VMR091a@wiLd~L4c8|w6igj`A|HdjTk6hEi4GIujN{h2<4FZnwoJnnmXRG z(6+gTi9i0<3*3V63iLh!|Gi>+B zI%ar}EI4xAF58$=!$>KHA<#W##*oCau!fb8?Iu7-vNE|uL;9OQ8f%NGU41a7S)vac zjz#JeyIdNPAuqzs8_GHoW~iEGY@QMVA2nLL25Q%Fby6RSss}}G18W+xx@#w-(A2%O ztm7b7F=7+s!VPk)fQd5&HjZE54jpAAWk-@6ld$+^A9PlR7jFdy|4=}&e<+|6f-~!I zd?!Mq)Fpf@joQGSm+&M0a1oeHPl8^$?zx}I^!i0nLKDO?-o}3nMFe~xmZXj0Bmp#J zyw`Y^3At#b`$v7FGYLc87m+fXAy&j}-k^yF+4X`tJmWNKVdoWxLkvH4Cu8CD=5Md6ESI$mD2TnsR-oNeWkFbT>JF_B1Pwra(BH1(zmSzd=kvz_z%2 zpk$_*q_zf(Z=XdhYZH6cmR=sx8>t9DglwWJK>Uj)a$`YyIH3hM7Gv@oRjv8!^W?^i z9DVJvDI7@}x{GedH*f#!CC~^}-6+*r9H2tkPcdi<#sTwmL+VYy&Q~Pr=UXre9f&%R zb!lwXf)$p!AcjJWa|_VejA1I8;Hi4e&@s-|K!9x!uVGyOjwmuM$mx?@|i=# zCtqO_q%zgN1<}0>6JG9(>y66`#3_;WGr|mIr4fWqcBPIJW(nbn-_j~^<0U^p3WXau$?v zpl-y;Iahs3L229kXx3enlm$t=;bgM8#e=+nWT4;(8u^Z0Z<}qpsx9rAf$U&AbMPH3 zRW*u{zp)sit<$@KmXGGV>xhifvGSU~qyk}uaiIjRS7e^x9)L$f@W9q`){?@n8k zvIMxs632&_*S{7D=k4e>%~XTnK;;k|NR-tn3!p|1O$YMd%9e!CKp1k5IlU$FZJpy# znf#Lp10T}~7^{79t7nUqdh^2hLt~PopJ^cDt%aVyXdq+=4O9T3fpY#28i8;Im7fKJbO@wL~TgDZ}QN$3tuncP841;DmcVNlaqkHk+z61)=89hHTD>V=Fvt_Z(n zpG+@sGr&e8+S1V((c04Qp1ia!&$L>!L9i71fZ5JRarWQFCt8t;Yc*X*Pf=;K%WU~uSF6n(ZRlC2$KwvBo zaLO2b?+B9|tk?gix~;DA9O@>oh7=A1*1uqGki5Io8P<`^Zt3?e0IAj7VkVgc9VbExZDL$fF! zOkX}SUZ%rs@p_Bo*A8yLX^by?Nf$jTx{u62g+BlB3U@#LAT*~O#50$m^PWQE8aHE8i~{uwT#?3#0JD?lU-G+?KZ!Js3{LA!^`Ew(p=msc zR*;d>xAe((*>ohs`?EozLUs$RlF5NtlH5>JTs*O4lviYh?p9UO4D7hwu!R>MP;pK~ ztC6u~u3s|4VV!VQ#y6Mwk?3Wh661qY3e@*?&X`*$S}bB+lWD7rDK>RZr^g3dL8r`8 zRm7F$bCkUBGT8h%T&6c3OOVc33MR|z5jIrD1du#^DU;FH6)7D}k4o#dGtqyFfxvMe zPsiP)&%WbT?9|;^P=*q$^07G@_OFA&u*qs%kO^>YsbTrM<*DBpD2+6%Qp~{>NShvm zM1z6ZJyp+BJo$gzl-4BX`@ya{^`C(b2;K+HJ+&;2!DGlnC^Y#g9N z|Blhi10$m2#?j%efQ~VE8HZUwC!H7N@h)kiJ&k)c?s&1dFv~?5(cxE;R^4n>20GV3 zXiN`V;+nX&ZH5cWMizLvPey5Ti9QO74Y|>dtJA#?^meY zMcyq;ZY4yj%N1$vaoHYe$)wzgSElO9-~JQzbD)L#H^RW5$yl><*-Y%b$A#^B?P`0nH;X^q3Lj5yfe`+}fuw%pKvDkz z4pggr?sF#CNh2oE>gw&4JT1qFUq1kwN@SVU#$%bMVA|SX$#)}_spNC#aVo`q@wQ)B z#A#u6xdg~%hNvS0etVY{U-9Egb$V=yq$AjylyrsuMNV>7X;F^TG>0iI+>>s(-L&M3 zG+LB=-ADV>3{%OVxL%z--Pv0QrWBwLpWDGpIV=aJqvPv6b7mxj-Cx5mZ@2eTzeMYy z33%GrT}@4Hk;YZn)S^O4V@PtAN4oDw(MY;@394RU0WmkzG8%b><%65CD1O8vQuxkO zuWc`9sWm=2z98_@sLP-&70?d&s+44KDP9v#UWd+9<>|zmj>vLGQoFZ)wRdU(xt4f+ zzPP^9)w?89+2p{Y099n2G2>R@=1EAbh^Ut%U>=^~hPWW#q zkj3fI3*t&r+po)1hFAeYuNmu3{|6}$(ffZX1xjc=TA498Y<6oEQ$m*3!C-u$9v~7) zx8OTXfvPTUk~wJlAeYM575Oa@{v|)Ab?!@=&A<0hv|a; za5N!_R&~d8MCg`*j_L~V{-SEkl4ONACMgR476v3ezCX6J*`AOTya-DNsMtcJ(~_cc zjj3fNxqVlf09R>gl6aq|i>#kAGvu#sF0a|RLcpf@LYG?&L$F-9+%G_t7QyX9gV+7O z*A8i@YB4qSYFonADmarv`BW|FIWfjFfh-|GN0uI*hZB=3`v$@j ze=&32(OCPw-Y-T?HW88-qpH9sXGb6jqU%K1+`+Lp`}hIldP7JYf@X^`%S>;)JyTt; z5z}(~lNg(^Gt=+1mUeHyi)#*?RCnCMgx+Y0OG!37mX(XKpVTSxD_F`EqYv1xWvP!7o-w1Pd+xm-V-7YC9tBc5 zeiQHl!{@LHY0rVNm~F7WeyFU8gt=dp_*{zQ8_qGn{J`z_X?IbpC2K1cMVBxjGJ0nA z(e1(f;tb9t@wXDl>fcfV$vISddzM#zY_w%U6lZpuH`APdgDcUzh9;bC%>9Zh*+qv~ zX<6U>hsQn;f#`da1}$Z%cw1dh=|<9WjG(ZrU!as06k4XZ1a zF{UE!eVS}Bee#f9h-`@LN{H-86=?>kjRWK|J7+8sFyU278y0JMNX6{&gD-YC)3GQ4MvAi`H43Mw2X%*+A^mp7s}eHugo+G??@Nwpmp|W!9c|hE z2OLnB+RxoFj52RbGrj;?*P_84A6g2!jW_s~ih&lx#>vIc(1U6KbxEnAktz;fY`<_Z z$Z-s>vxQXokJ4w`qi4lafP3+={^Z*m6>w{p9!aURS||%wv7(rLnJ9iKDg=&UW27tP zCLw~V4WRYH82i$V;1f^wCr!4z$fRxceSiJO&MqDVj`??uKo606?$MDCqeBZ#??q|x z<1ER;RiNt1+8bnx$&?7t`{ZbO5tP=i+m19N3Ryk+jH^f}60oi2yB7<%rT_HBCg=Un zf+}o?E43g~?K}{dcrr7Kwpg_G{lh^35H3n@o6 zrj(Ts@-7)miQ$Y=NWrmFDVswnYdGhdpA?>TNae=xo_trK5^}q_AKG1FsQI#dPt%v6eyq{`vXQ~ zmKx6t-Hbtjq0V`-xchF7mQ06S2SNa!ZkjpF9eNz6E!%YC1Q?JH(xStRYzqysjFJr! z+w#}*@7VYC;0%AHujCeL-biC_{53|WLLvGr5{a@(d+2%7=zgvodxAWKnbTcLH)KH- ziPi%VtSmL>4n@+vvgokOm=2(O)Ci;Y;0}rF5dP{1r=hc1;^ld6?)6%4FvQFi737+{upxp&WvJLEC3Q&$z3Z`FXY(2d5u-$(JRvxN?~}LQ!xwke zPDows-^PFvq6Mzd>Cn@@>v3hGA*wL*E|~WD!0zC`Lfi~M3xke)7D6g&N%e{6hULx< zlN3q5{tTk(pohS!D9&g-s;mkD zDo{l;q_q3f$6RF1`P-u5;l{l_5ge;fl@OJfn4!bv2NAQwb%V_VNEWf6SV=>j8%ayk zOjbjtXF_8OgDh5$;MJNHetu+>HNG(q9Awp1uFHv&+OysV5}dnL^;X{dIh=4Yg8+$( znFzAd{XH&57{2653UN$vrfTL>n4YkO)4i+XHpDf=1&la8DpXYxJsv8mWQJ%me>ui$ z9L|9qW=A0Ee-7k*!d0_1bzn`@Eas$M`-&Gv$c9z>s&*@YUV!P+W_v2Ay~2Z^%DBtC zLvM-D)T|k?6qPWtHVdQd}%$h{saNUyIGo<=&(5DraZ|rWYca0Q_!?0 z3c|fCF1J4aeBCEr;AMuS@~ZGV?!1{8&5lZPF621A)ZUn$o@+?F9`s*nv5lmEq{U3` z>RQ^kqWM~9#{X?C&_&T%@ST6$`@DIe0d%xQx@bt?E8{9Z!4%l$dhtJcr zHOD-dyNSC;9KDI#$HBpKPI_B-y+lDHF1q9wsBQgV{_|jS{=?m`eFO)T{*lD;NpdrL zWt8t)Yy`hiByF}bR^@O*dRFm#*aSX=-VluwBhmP1nQ6Zutwt{utxm=(>#Nvfh;QaU z5Ce&wYwhmKUaWh>MQHuUEYR$`D$j?T?R&2~EBEN^z_2W*K-{u=6X_tKVHajw^O}Mn zT%=bo8C32L1Ws_U1%yQtn_(F=vbQT07ZvYDnIuL@r@s)G{97~-ljY6Q;=QBimFwnI z|JBRf#I};{nGw)TFHaH4nA2K9c4o+6D|tUQil_NMgrLuh3jZD$^E|u$9T-cY3v2?R zjA+l#3-D7?lT)XV#+zTA-Ho@;-cZ)V3L*7RnyzVr8^1be*A5EN2B6g&6TI{iC>SNo zNH)xm7|~`~P(q|8^D+!$DpaWby%wUR+CSK|eZi<~QEmg5)@Ce8D2ahDS~Ts-_#+X=i|OfLyUdPNcctbwAc z6f7wbcxJ1co`CEDtA ziim+#T?iW-(!vPIfh{+I61ot8oVi5=FpQPnQ+^f@5rzKP3!@|gsF9S`KuYEaWYUC} zY7=hr3s8SIV_P-dljFM7Jp2D(20E=V8X?frJiOO9tk*i)*%|J;NQV|{W693WiU&XJ zzZV{x6eGN*zc|v`%wq`Tp#5Y0mX(M#{@OVd2}&8EscC1vni zA%vGXIHf#-y*ZC8vQ`Lj-q3)1yuP0BnLb2x+L4+=F?@o=#n3!cz7Lr;OEo!zDV?<{ z07dgwUOAfYzG%z5Yy0l~5RB4Al}Rehxb|dryp*~i-stl5F3S*4b=M|tf97h%u4VNY z07&N*BVb;$XQ_FKRFZn^Wt=whioR^-^ROlODJ7{LK{K)7sKM*DN;^uf%}KAVX^BPI z(S0?&dSqWgUZxWFR!G8(Aky`+vfj1o?R@5H#&y;D5~kWOBc^cI9HC;0Pl^|IZ>d^kHA$}1>hgx1XNjq-$p=*9J zTARgUc|+i5?kYszCSq#~|5k>+X?$5F?Jv(0R z?|Td|$6ay`wCc>dJF4||NbyF0f0yCEJT}t?S0}XT{{E^qiPNlE?F*_ej_zt-cBcRi z+mPPY`Dev%SH?_;%Lhdt&hK1}TWXwL-B^ z{gyc$Oxh4Hrkc{U%Vpeu0Z1gT3Coy>T3$O=Lf zh0?Ir_(#_BTZy}oI!jX*F8ZX7>Pd!*b|b~}EE^ca(}ZB@`NpIuMuKm0iL2w3z_h)x z?NBD5u~3zPJlP#nq8DytaaN6lSja=x?@Zo#L5n$M=33-I7drLkQ4O+?;>GAU82C;{ zuuc^lT8Yi`Bwcz_M7aVm{(uISe$k+H&tfeuKCK-=_K2W%DHNcNW`)e|v{)lUYMJ@{^OJS}E<(WRT$ijAK z4CyeXrNpVNXnm~4{Pc`}{BVHaWT0<81%Ylu+|8V&-$t0WVuxuk_a0OWD;s7P^`3Qs zBTzkk{)@(>8CMsGL0`&Pf6PScWui5TOsNK^qC*$91|~gHB5JgUlTi;K`B@ZM^*v_IulgtgOw`rfw}Mni2Y`W zpe11d(4cu6#1~RMxNDL}v`Ds2+d6_7QGbGDaL+-qZjk-tmL(RzlM{=0-XqGx4|b@9 zm?qC5f215%P+zgAy{(hYK{gj+6XMI$7mWQW5sQk=Gq38)14<2skVG@0jdfh^iti*Q zw=X8s^$u-%e%h-P%&bI(gu5|MTCoYmGGt5UOO6#tVnk9TV8WAqb74`U;~{ZI4-3FW z3PYWx0B`*Wdl$x@OZe;(qMf8T3!80c@MkYm3Rg+Aq^&_$VRMI2unPzNH+PBI$_(0Q1t_GR;L(*6U_D`kG|sP`1c5UdmLpPbiDd4WD%EPJXUL{!O66Y!-gZVkNGkE}uoW=RQQrIEx)p^cZy z!4Le+5z!9NBbR8{EQ8xWW693L6zW{ngXS zxN{9l4BsqjF*~Lg2({sKzohqXuH@Qw(PGmjOy@GzOd7rIN?=%%hO4|EO`vLTA9F1t ztj_wC}WUnR8Nx3rnlfpwO@fPmxe(PdhnOGO^n~-Z*Jh z*B4aKSb~_Cej?3EBPPGstX>gc*?q#bR7`B)%Ar*-U=2f^h8{mY`!>)~I`$-0nu1$X z&z?_dCQ{+n_GX&aT+xX>lOx0$Ws|@kM>cGThLlcvc&W6krDfgXTQ0d832p(ZWFo2^ zjg*cW_qw|Pw23q~V>Vna3Js&tk0AwSClu(9nX|vaL`jK z%5i3J@B;+|B7(k{qc9HpY&bHQQ4vb;mJ;jR9E`cn*;aqgibYd4Dxcx!yy?`?Ip*vJ zm5%dzFy41apNwyaGdQv4Lo*w%pDrd6qpCQzDdoy{$qwsbkaPfh`|wB4(gu-Wn}I_E=fD?G#16}5K#t|4{tEr)vyQWJ+?eLcJ`t(%B+DT=Ufs3UzR6+>CM)k zXv7f{Hqsx|CRGt$gl3u?<`|^X6UJ7x>y$vnOxfU2t2&N-q}ZP8jofU!wjS5Lv;YsTuI76IuF|NiV9K>^MLSyv(;91*;Yy2`MEG^;~wDRPEFKJbN9ZW}xx zMSDt57Nk{^j>dTrlfS0pL?j53-R?aImz)elmmc})OcD(UbmW;LQdf>SN<7RKc zb+6H-#7P2P4$K`Arhp_$=Mq8b_0Hq)g|Nz%CA>8LX|cm9F-nnXcPbcblrrn1o~(F^ z7B3-Ep=oyKMLTw(IEHK~fGX}pe#G~19onb~PL6Kla#M)1iF z!yvbaaDz^M#d4NT{%Qo3R#t9$IYB4s67ys=%b#r*zapBzd)@rWS;~q%r2tqon;|uh>SSv=AHu*+Eg(&=N<2kviaf zo32P>eLvnapb>;1icQcFj)7J7qoE?Ai=Y8IBo3uR@?>re63=-uN)P);oCP)B7V-Vm zh1ewK)URONwJkVfFux|NE~G0){iOtp;!}Q-)q1^taSOCC3^~;ztwJKZPSF&!q(fp} z*;tmaT^z!4Cx0(Rk8(%}&!^CNU+Rl{g9 z>CG4ABZC6^IQml9#(t+L%4C-s&;&j}dSWP$o*42oIrIsx>EROd_c`SThFJ4!EM?|1 zIs^;SnX{QX?eY0ke(xaj+!NDqT&7z00T%Jm#Q=daqNysq0gW@*c5xrE8D*snsEayy z34fDAUn(bwI`?4J8lmW7*);rN4ZdKQ1Bne=^Rp zjmSZ)w%E{k4vK{xWmcxt;c3^EPC|lW(jOq?P>vIRDAiv9G z0T2^_r=+1aIwxk7{E}%ILw1KlDOx;X{w8IR=Dl8IrDpd>7&>jA1gp%?uE*uxE5n>( zJK0ko!Su7%TS+0jg%m*yX;oDYqx|ovnBlw;{z?1Ay zThJY&STw0HSbe%25Q1=yi9DW&oq(nd(2hIOum%bRGi3L0ypQD{>u{|HLFSMj&3;H} z8U+FREJFuLlx-M!YnU${*R4U`74iz7PG6J}Hu_IU3=A>$&)w6sp1DPU>A*-YF}s z1W%a{D=S_d18E{5o1Guux8@xQtt57cVJ?}|5nwq-UAxcD5<9~qICbo29ehkVlWRJe zm3+Dyse$HFBfDanUw!lpo$1#Ex1{mx+Z{VUb_*^c`I*zzsi8I2&WEmf zc8#Qok!5JbGp}#ci=%w24oyxhyLMAQQ|<;9xl$slAgf!PqDNXvOZrIV4}7l9wUEWa z=rX)7JBRMMo%^P`h^dV{|-Nr4l=<3Vv=G(U?8hKsY zjGK?%sNOA5UE|t&V55xp)a&Eus+6;iuzjti(yQxG$x&J=$p1F0l;B z(Y)phU1h$!2b;OSn>+Bp=jUf#?XW#M{?c9~z=^EW_vz}qKFg_h^pa2;g}godI@+lr zBWECfU{spmc6Rjcs%wj|Wir{bCu>4byMnmh75U3ScI)am3HTG5?XIBH5s$5L(3ET8 zU2fWPI+8u)o6o`1R&wsDI!n?N!ZOkB2iTL5_cLeTczzI~eDd>VoOPa7fli}C-qL+E zPC3Ly465s0A|U=GVL+-0@Zj^@V@LyR94$evYUG_z5Z|r0!rvYprCJR{Q?5S#I7}NL zs`DO^U9`rCpfF&Cbi|fz%-(vzwNhUPB-6w+x6He?=0!Lop1mq)6Q#k_h=4g-d2j{( zbbdLwH7tME0=lh`lcaYh!X*lego*WZbbUBm1`8uBNBq;OWs^0yO&?9~1hsqJ;_jRfD zadET@BljQOT;WFcXt%(`k*^1gCSW!4r^yPe}GE`$|bu4Zc+9 zxchYfYa-ZR1oo2>>b|{ZJQpJt+Rtyz0><*KC;SRGp4J<_uSp;hA#k4y_HqW#Uib+$ zD{}dUHf!MtL?RUO3rZccD`DO0%JAdU=!@88>@{h>W(pnXO2j1gN^=RNaK~oWx12UQ zp<;BDJX#a1@oM#kA-68-5mwJRv22`g(iHWl`@1(caqB22CZeQ*bTnafh1Jvo7SBB~ zRghZY0Hr(~C1&N1E&D$6if;Qp4Y{v4-npMPT?6CctD!V7p>cai_^;0^7h6d#1)w!B zVR30oPskJ4s}XGqIJSLE+z#9Z>1cU$Qz92`T>5xHYdBSH6hur__}`Gtff!mmZLIE8 z@AnGYT|7wZL59qd*l_-$-SxTjSkgnR)s9Z)8!2?GnB{|8`h6@y2GtuOZ*aGqFDg$` z=zMU#O4C-KKHmPEAD^g;ZS`=0)9|gw{9_SP8iEXrpNbXqlaQp?yuf}Xl$u5b|6{<; zVC_X^ath(E{+ckdYGe%{hT5Kh>!d*J?cHWmmA8+o0yZ|69q`Wiv*BoZTLQQyRE93r zJdjF)4v>zLhXW#%vuLL?y(Hm)07QT7y_@eMpC+EDGbi+k>Oo``$rL7ifQ)*>fN$w8 zJUTa?_6uzkHY1gE6zwiCinIzw7$bgKZSV9rNVN0CnzZa?XqQfF7q|JAY;8IxsMc`v zt3=yGu)b>;&cG`=QlOYoG6Q<0c-O-0z{xvP$dl?&bR}Ph;-V$8vUJ0^ zqR0E(ze@X6=Y|tj{iuI#ib*9zA}tfAb5<7@SJ+JD;Z91!2L+{zP=pJ-6y@~NC;$FR z^D9G%{cxJ|soCxn_^RtCiIOI(Q^d+0Mu^FwI84UHxO?7XwzF3~&k942T!#IsXHhbn z5X2{xigW@k@5tY3^)4;@_+2v~6kB^?uOsgEddBPlO6QamjS)JjK2wI;p5$AyOiaqg z)qZ3aLN87;tPTh!sqJp$YIG0fnU_X9ty$KL~Zi+46SKh2)+Dj4Y7@}2uCb9jQmD_~Q z7@C2H0(k&HNzxAmMvNcaKh=o{S4pyn|2%g{#x7|`bcZ+JyqaAX5VAEGq~kcb_%q!Z z^b8Q``$=6QVM1Y5Y2(QuU~RSg$$-{CcBlITNjcZ9IhP$iGD_C|dei3tAX~RwcI5)C z%m{RAMdf*#)cohDnEo?Cv@me%37MVwz>&J_F9{+Cg(EN>2`nt+G|j(-juoWwRjN%_ zn&de|>nBg|-NG=We9`&J)%ZSC(+q_yrR4+aDWX0sGbXh7!Q*E;Z!`A$+9#5?25|=_ zdecMi3>*Sbj*3y^Wzl3IE};*^j;&p2aX*!rc;>5H`jl9Z2u~`}%2M?{zwm{X1;x&v;&5U^`Mlki+*zhcN%cs4 zWMfodST!LC5m_AaU;yEJ%0Xu66o3%rd+*69vqkNJ#pAmdF;do}0O26;^7m>1Ys@4X z^wYR4*cK(D`@-PDQCf15U_SMfNWLF-QC=Tg^VU0K45xRvP=em7?(sb~>HI~~>#Y%S(qDKYsx*+M7^Q^@#uBX} znkr>5rLNhMnu-HIJCw2SZ-l5Hf)GJLwpJ2KsLHx*)q3YXu<`*~71KIsa!Un=aq!f! zn3V^+g!WTR7$S~peUJVI=6vyJ78LbsNw|CvVhM5Vq1S<~US|99k-v(PY-s_7QcnEu zs2HsnEmv?M1R#Pb`E~BLT=z=Odx}-Q)ldL3W zhYWHilHSxV<@P}q1mY;Ty7ZzQtnFc3kU~U4px3HlmfoTZ4Fcj3cc0?v9#cpbbD?62 z$~q(~kp~H0Q&%zv&C&wnk(NCi#O#?-l7See7^DZS;~T>RKUaF3m}UG_1#^p$B^4G% zasv`nK$?^Tl#Er)jjWK1vdxGlxa%>kN)!?eH!XCiypXMNOkF(}?pXMhm$Qy9fXTov zpBQTCIvk58f3^s0bzq8#qxA-FxW%(jXpmkh7a|mz`LTlzk7;gzVk#A=8P#;YUB;yz z?ahDh(rK9@Y*EB!5Nf65tm}ir@!gaR#}ag9z6QngUrr%NA==S=p*eRUk`8;$W0kkW z_ZcQKhrmQ4deGJRF!ZGEW|t?iUaT%R2LwJqG0%b;j?rFrGf={iehNwIwP+8%oDIan zKqU;Ji9#>wfcGAo5Sj=%;CYk{fg9Xq-+Sq_^w7aUig4pnn znx!ua@)&E_0!1U;I%lPRsDg0WAjD$+$NT*X*Nc7MqjUjCcPK|x)fg%k z&c{gzf0RX+gLs7sAYLK#XRnY#U#pJn=3S3TC+VNDDSwsq;Pz;J_bb4&1HLr-#%t-B zC!$X&W_^cUtP~F6iR^QcwdQY=hL>yhMjqk?iRtX)dsmy>u^f&*9-M@?IdoYexMpjoCp;W65wMAmqAx7qd%ue~DGS z`-Mk1Qz?j92p3`&l1bG>$h1*S`}U{T9!!NIsi2hI~X=)tt$wgfZmIA_1Zi}2%3!= zh$HQKGLw;^qa$#8vHj?-Anli-=AsCL^4_hSTyt1P-Y}CS)uoi7;h8D&g)l|u*aEYh z9l&&PSj21)a`Abl*PX-Dz|^qk9a4sx(m}m&sieH3)OXm}iT=bgq*UTm?&4pGenZQc zPET_tMo_f6T&pe$v65AA#rZI_N$%ef)a{z!)!#GSLM6aXS*0!g#)^2vAmevq*=i9? zL!19*X$vcrx#Jt@jfK} z+4EsFGt7dkS_3y(Lq_%mL@%`XtQQibCrcC!+5WMtJ$zH=DIbm43o;~wd{6ch?e5Jj z-|7`5Fl^yrcQCYo=!JwKdLhDRy-;LUO9W5Q*92qxKF>Yf4o{xu##6}`>X;D-ExBs; zjN5AVLsEvUn=wjXPw&=xW5ouHaO0NJA$}pGG^~8H;fyZ&x~5^yck_wZqPPk&tn-Zz z{+m@I6YXlp0)ASU5Wf&@F0OQ{R>1ZojcSyZEt))koLGIT7NE&@+7O#U#4AIXUDt9Q z%|BU)p+sSLlWP%OFa6%7v~mecLB=m9NBp;7NEf74h(J}@FsG(Ge4s)^+JG4RWe2mI zj_^H0gdjt9qG<8<6xXMrjVRal2U0*K9uAqdZWpwYg3@1 z)`A_vKSl*Rgl`avpgqX1vS(=n3`N%xRhqYNg1;ZHw(n)4t}Z{p+e)$26%>CQOsOvk zw5mvqUgiDv@z;Uj-2-Pa zuC4JRBvL)er0K%)0^Cpd&X8}1tjvA)g2 z?L@)znwfi|vl5Dd+AHc$af}{;feWGNB}3!PH;Pcd9Jq#Yujs1c8w`s2UbuQdUVZv& z0q${41umC3+D7j5pEDIg#t=35Vyf& zMPTl!(R=RZ;`W+&8Z%8R(KIoad@R(w5f? zACqKtq>T-e4Q5$1J34N5uyBR4F;nOw?SSg6cH0-N6;(c%f>NIpgrhUV&q#xhGml6! zp`?}QZ76l)t!4NR6X_k(k#l4kjZ;Y4-W81U^04$t5+pYn){7w)i{AIL>&Xh$GBg8m zZ0A2$37|82*?y}0%=ndo+9pbdp?fgJ6x*r9#La%=(9Wi;L~QtL5Pd&VJm;Nyq}96y z>iSN&hJ0a$_oL%0Y3k$dN5LL1EQRhCo2R>BkrWf3hh1i~aK&?Xtaw%!etP56P1{K* zo$>RiQz_nlplXt{yP5GnzN5C5kJSpRwNaLDoRLl3pA~q=cv7r@SYq6NNWF;Avw2R* zsXf|F$%*ymW8(!A`po@aa5uD+47~OTw?JyIgVK`4p1j;d5@@hxBx#dpcbMfzKdwrh)T)wXLN;HTxz|-VZ zz4-EP(s249&cYy%G;e1rqd62@36P)`oT+%L<$aUPzC)mInC z#X3*?GW`SY9)?33cEuMDR9DgYg4>Vwx`fQ8!2+wAm-RbK``VpaPJ2%^bseAES|CH{ zX7#>mJ@e|&Y|0Rj@ODrM{nt)}SoF!&;Tt)>bowSnTM@nWhFUn=$*bX!!NVhGR=dU# zEUm3Fgyn5~+im&Ot@pS?>#I=H= zC(|+~Tf=QJW9T1~lf8_t zZb54kY`czR2Wi^S+K-+YN4a!1Bex7(jV^%-#Oyd?!(fCEF$!K$5X$U29~^CPo*BNi zF`Z8z6u67R)tGkm4fp1%rdJdLJtIP*Za|}_hcaf&7|%Uxb{8+ozcE63&x{c3Gb6P5 zH%7>gQgP`pUoiObdxX2A$mvBwWNT+knuB_4ZFADkL}Y&zuG7hp3=<7W=W$J98Lb0x zwQ6ophoU~lkBmA?VE|Vp(Z`5aHQUZ}-cPASoAd2Au}w~g9M1eyKJIk*ac3p2;hzcF zXfAx`jR3JJAAZJ}iw}9oEI=!P!#QEWXN#&A2*pC zx7AhkfAnLtk`)Z=OSEjCKh?b)j*6mm?Sv?UM%K{tx{N=U!LOa5qkid;&sqIO&&w*X=1o~9k=>-3^tU%P1#32*LgUCV`)bYA}$g|ea zR!8N3Awiu5F_fI-hf+1eahzgS$JSu@E%4ySoLq0Kwe}?oOW~*?Z5-H~Y+-?_Bk#YIS#Y_3C%k z1-}Z+AijAiT`_MUYAKnM5YCEex}!V4A0}`WD-B46h#i=9 zDiX0`IlT5AZN^mszvmh->2}f>I^jAMhBco#F$$t%a7ZX}bijX2j4g}i$en1R>$ymI z$60?5FiuTAEocMjMq|~eASZ)!wlk`)YU@4J-Fs|Y>5|h3Kb^jS`P4GNsd}70kjKU zV5UR`Rb39aL|8e0+P(#*NZQ8AwICrvpLr(DiC3ILRAy|lmc}o{Wd=SpFcfM98QONE z@PGEp8T$xBqXPvNf*Nq6C^YiVQ5E&O69s3%S=J;>LjepB)WB-be_?>!{xU$$u2;_t zknDeAfMBA|!v!Td{0J3n?c4r9sY&nLm||XsmQ>s$47;kpvShifHDkQI5KU@G=<=A}baSVTlBI z;3uvZj)gd{c20%AT8_jS3E73ZgOz$NULngb)sGvz$-e#DZ%yF;N&k4f{OC)?{EN!8 zh9~1dKie-BgW4~4t`4b3x?Z}oE^*iMOH7cQa3uB1$$B8WzOU4OTc6}>TmQdu-l=UV2*L2#V6@O>^i1}da zXQdS3{fz6X*Rg`@Bd6#jpMEo$F@M{ITkJ^LcuJ)(INbF9KPI0V>NwIVHP;4>6%__Z<>RNT z8<+~VwuAGb!-F+%vxpgAsL*y3%VH(rj@B#3Pu_gwbp!w4!!DSGQB{3M>Qfw@HhE+~ zG7&ukp|9IdBAa2??v6@7N-4Ic95@cXA!6j<`@EO)CMtbv_KTCWwi~=mT4+hXOqlQ7 zJai2KSuUQMGdX?Q-!RB_C zr^~E_8*kXQhze<){PYIXxur!C9Nf4I@II%ec?#_fd?WZ-3z)!Uf)@CZ-`JF%B4;i`;tC2S7i`~l zrm(J#@Q!&M=$bO0X0{97{XC2ghWO`2!GGHvFE%v{X%$wv!x^i(8s|{Jd)+^yR}3q% zY+Wpx1~#`vD4a5M+-P70O$Ya%e2JGnQf)A^s42mrN#n>P17Bz>`kEk%szCpNeO$$p zTd!f@iWp58aKAdne#;R2E-m*GEN+7;?a)FF^PC{2f=Lax^KAhNY{Eoupf3(y1Lurb zeg2F31Q2UX@=sX3@&k`Z4Fuqa`jwlAQ^oR-VAorj$dVQ6>c~1&UzQrka%_DAltB&X zZd|7}v;rFmJYh9GT@kgpg*8wia@3YYjfec-G6(R+X}FjoKB`D&%dq11siqJaa;X3# zwhu)R{?A=EC554I09MsL#ExcTZPHpbcg==2q2jZ`+*PvNI}>CEEH`lD+Mz2D9JX4i zNXw<7Rj(q6u>(*()T{O_r7%I#3fiFoY=@N=r}*>njCq9$IaBOUs-F>!{T< zI-|xM{Hb+1Vv#I2WrMAeKheCvo=RpXl#AI6@68^hkoITne1;HvU_Wa18PEj1bjYGpn=lvfi)HPQ z-jv2G1fNl1tYOYRW70U+5#NS{XU4Mut7W1X+iZl zG^~ud;jlhwwI9!J6HcJYms&ws4QyeCNYhxckhet-y{~z@k@%b$R(+Mr7=%P*` zCz}|`Qy7Y9Q_Q$bx5uChWQM&{^!m(`6ECYh`uRgHzxO0!i8Zu3l|>Z-^Ts6;zM{6& zParf5{2Ura!<#(+57NhY><4G>JJ?wsdzNQMkc5lU%y4H!sg{}r;0VesL-+eOP8r#Q z{nrunf!05-PSA_LRVL9SvdDpin$i5T0yd%C?0cw>=g=1zq>K?zsy~QNSy^UaSQ)JV zN02ltV_2#Y%~#6ZZ=taPOnpMkHF&AYRBs$8gG^OrzAh4&b<4TH#<@jJWh=&aT|GLW;cib>Im*Oc60D zRY0z|WBDy(gFDrC5KolWfg_>@AbsHDn57jyZyOY8hCP!$Q5oNDfrq}UJ(E6A0Oe}PcS3Csm%tcI4Wx}J7#=`1=abI&%DNU0RBB5M)`SZ)&j-5%{X1a zF8Q0AF%lOm62Y$$=GhC>@RcFz#FxsxbL%XH83nIYfrdvxrRhw9$L;lx9hht#gFDn> zyBkza3rkDn&C2ACA9$<$(-NuX z@56Tiz>MMBsui<^{#4s0)CS4BcJpZ5hRPR>J_Aoz$5tiNiJ>*ge>tD`@yd&%TRa{l zJKehPe?7YW9J1DvG`wH;Zy^+l^))>V9_5PiuXD97RtX6sKrf7c#PZ=Z~xDs=h}uIfyX>W<3#Y|B$u? zDXAVETN;X}RQu`zXFh31ifCBMdKr-S^lAAvx;ZUE&?iN2Y!S5FHo($MlwG^lD7CHo zYib*vKC#2Yowi{x{d@eaIiz^l=NuGIEJ@(A21#p;sPASbc6oY$2tSO^fd+IgfzuSP zax9>25IfEak~%|}?nnh!cn76UM`wi&mRBVSKQSy;LXthg1&2zw2R+x9w?2-}<;}Rd zmwSjTPhw~A66uZ-rTWAO59z&f-&tDnQNMmk$h8qIY+|~$<9&3+-n7%aa_7-qIJ0z9 zya`^~)gy7KzHT{Cv-qvi?!rcXDMQ}6GI@P1;srT-JQ#@Bxpr)C{Zi%s)534-wOf31 zU)lijY98&-hnM)mGrKM>J{z;=^cjb@2G!?=Cnx=$_f3uM`&a2tvkZ2)GfR_`-lLfO z3s%;wffsD+pwVB>;fcoKb= zFBajeZMzzG8Ze0zcao1HJ~=zmb=1Z#RYT&8!@cz-t|^YUJkG9(cSevf!fy-R$zH*K zF#fJ`GWO#x`kSSJqp9EGPzFo&63l>I;#Z-7>?4K)>Ihi7{PI@QN%6(BdyjKx9=$+g zkmro(%F+kN!<(O*3*@a{=dQ+AG07GwtO3e2@{)1inwuX>`pOOP;W99|X|eeEg=}5o zPtgp?vf`5%Bw!+xJC>S3279b7P_FBZStxw%XZ!484s+HKvecFVy5GeQ4 z!}r$eUECh-mZoQxVsval!zHZ;?j9k=aCXsqi=d;UjDy5zhWLdH->>3;9*E>%BGlpP ze942?_2hQ__bGg)j5SZPvAx);IBrK+_zQa4GA5gr>|2t8J zUzmeC`D5fQBFn$4mBw%o;MNg);^2DXJl>-aJ}+%iUfoxSE3nI=wJ1xi*qSrBL+tCI zYXfXMdmpI;1F;QckTe|>ZOb!SyiP3^+JvAJq2zpNkqrh)y(U64M;lh!3|S}&xNm!w zjvxkcsSBn=iu!Hf9UzKkmgYw$T9Sm@2G?2zT9lBuwu7nY)^%kV zyt69}=?^;m4P_^n-5mDb?0$xdXWpZCpCO#O>xHfitj5dkpZCWh0h5oT#bX@_u=WxS zFn}n?LicBM4jwR3=WadUfEA%G?@*Iv%24|82gc=T8Ntj=AWpF;b7q40$E_c0w~tBe z&E@*9z3xzJz*!9{y73u2r3`FenPixO388#kY(xzwhX$h>b;Aau61vrJ(9fpR7BSfu znoulZP(#0Bic5>BrO4URA7LwK5Li@IH{LQ?7+}uJS`9)EUCds7(NJ}0;Uca*KMns( z)5)yoCc&qt$N86(;i{qsk$dg6`dk{9qif4>63~SEzo9bJR!&5uH6_#p<}gl&aS0LC zO&oy4uuKk3`yNc>vu5|1n_=7zBm-Jp&Da8SCn~lsYJBa(3{5zNo62vuwj*lNb?Yw90kG=K& z6YFF4|B>}^aDUicnkK$J>8w7E5oJc42xnALm!FVfmP8l{OX^NofzKadp6ESjqMC?+ zC(MQ6#p`4#__2SkwgKpZoQmP(P5^{HYaQ|y)>^DjUn4)AitC#uJ;d5-5 zim9>K^P~U^lcm+dML|#v*zY^#&X@jmr5CYLV zVmU^X#fl$m7vP79FX3Cyvqw%p5q|m|cz-1HBNg#?^YN5?D7qaOSd)D|2kUj+Ny}(A zu>51|ac!*hd+S3j9NR5Ae3Gx6_82XW0Q$#a+J5;{kIUY%7wbp>vLoXs*XHSYveq|H z*?f0$gbk29FhH}`3%0dYFj9j!f4UsP-=}%EJm#^*dkmP2`N};}18@%b%27ttygo%q zy2t)uZffNC8~QWY(@6_2-|FM?nYW5P5;m!4Gy)EIbMTa$6GIY$Q|e_gYLsn^658K$ z9>lF(yCk6>fxOo-`k;a5C2oP^mi2a8;&_1Auou|!RZ3r=zPc_oeSGzm;uu#K44c%y zpP!L|cs1`0xqP7uHX?q=%A=2Wn#9gcX8Ltz;%N}^ZBX$iO)HV1ISg^7J>5@{>Ej{d zjH9BT2V~-acg6aHr=Z(Sn~N8hfIB>up9)N_iA-*e<2-5PTY<0v>K^%*-jrwkH`vvq zc%od6%>ddexyEl)lX36=&hT`FGWgPr<3pu?f-%gq(eEHs;Ry^EG z`mfa>(v^G9(P1z^bXb+aP$>Ec5FIA5j3`Zusq%d?ok;lRcC$OBI)ox&mQ{ZyMK#b~ zdqE|K>ow1*gW-&#>Y8>)UOy7N>gjLBV15xq3E51fceI$Qf*V@uk`? z%FXf3KP*8P(afUc4iqx!e%X3{8H0-2Zwg#YAPp;|vxl}yIRym$?$sBh=MU?4oSu=Y zpV9Krc0KI>|A+U1Q%(2!U6=t6YJYv4s{29}Yq*eBjmKLm7lkR9xb!c#!&lIt{_G6G zQ6&h;QOR{o6mmTfCQ=UAK-R|bTs1IGrBLM;h#!EP9*q}@ z3L8XKF$4dEhw+N!CbGPPD*z)E9i)+@>m46HdQJ#R1U%pX&LFm;r=N(~;WULPlcw*U z$ra_GdTcc=bwqL8uoERjRr{#phhEedf61z$!x@0en|FKK%MUIB8_BZHW!=Om{EZKj zEo1_UMiO^UMOmU+RE2Wn4bzi<|mQJru65EK?>|{7QN&Xi-FnfC>JcOl7vL@?ng0 z^HpD{eS5US574Bs4i40r(=Yr1HE6-?7e8=&$F*|oH2HihOSPssThTBAXkK4{|Cm{?D>a9+?Q#8 z*o+H3!n}ilU-!1`O^3kT&%jy$F$qs*69hH9c^a?25thp^5C;QDDCwn zR|D_|)Bk|{e2w7GxEZ*j?{mNJNWhT`Bq}ar$WP;GA}NzcQvhPbn(c#NQjT@#dF_6X z%V9HI(rbBJ(=C*Ajrs&?OP?{=J8bmsi^3VRDLX^b4?_o+jkw2&SUaQ(u z;i#p?e{5Ij8sisPnEoz`@LXmE=I9xuBCe35`(KmKGz`F`$T2Ct0_VcKnWVosi+n#K;$JEK$&RCL|G+pbKNM42q^uSmbZ+ER>;}zAOGG7xOU0C=9}6tZ#zGtcPIVy1UY1d zn`tKk4k2Y{> zbL4sJd;zs&9cSIP@<$}Zphn2^n#)GF5|c|)g#^pQAD)=Vq~gZQb0hoBOSs|8cvXh2 z)5{p@CHtet(E?;ynTkmyc3FcpnHXLgE4Kle;mNnyO0~Jp{^2;n7^*lS>Fa&-z4d{U zp-|AODuDQFbRXX~qjgVYtPCp?Y>wO;^ z@r!q-O}D)P;o|PB&%~dg`wF4w9Pw+SC)WzpcQPEUbpu}N@kWXX?^OXHbGfxHEj3H* zRhNRucf<|k$JmT*Kqh2|BtVO)*vOGKIivKeytVZZO#n_oJqwAci%ZRcXngaXoh+%l z?8i>7_tH6+<#@Ej<@taxDHV*A?=(t|xJ==;(i8-N^mb^vZdz zmwo~4-w$Ic6QnKjNv_+cby>vqRMHn z<|WP*@`+etqJ18r)zsig*Cz1O7G({4H?(sYC$&?4Ws22tcKn%Z`&vNM|SYS2r!??5jP4EFRrt>>wM6X zoSj^FGOI-H+tC~aR$Z$R6B)~7vecW8X1~lVl_TMLL*qpo9f6lwc}F7LHe@E66l~q| zft2&#(zmsHzqFL8^emXOjL1CjN|kjOx>&7g{J{+=JzeZlW2l!_#Y`yZv}l{}`k7C@ z2u7-pO!qnV0zvA$u1T|xvF3i57T8rcz>{m(KdJ+_{ucalcDcH>XfrO91s&0j1 zb>-n%nnF%B;fp6KM>T#XZ$m1azy$++T;)mxV^Vla9*-kxI0zFQ;maaAI;O<2&2cyi_Ljz5lZ<( z?0WQWr&ZZ!TnIFOfX08>ZnCaIh24!(9X)V%Il`Y&fdG@xuWRAS+z57KE?=?nf{TZe zEeZgiORZcBnIB)1kvo5}PBF3IKFu=GS+}S+iQP~|_)tX)bmJwX&GbMRqvT*m3vM>6 zm?-T!ecr7R64<8md3nOj8oW#82XKKsV$4Ws?s=ia_ea(OT3kzI5P9=`>*44|nJ3Ku1^09JYbZp=L&IE0D5l*{8!IO< z%X%R`&W1NGx9Kt_MoyRsM~o@iUTE;D^F*LE42T)42pj1^NV9nyddur`_uWnui$aD; z+99j$$kX%maj7`1TojDs(G!+cf*Z>b|D`svK=l#euERpSVfr@(bY`<*FoVFk^xjjV|o&#JmCrGZen0mKYtDBEfUThgxTWGGdZ}iscd^H*r8ja>=9o}RN_=Q!= zB9tr=J72TIw*tRxx<4n4830LRNMf>K!ghr_> z?WeK@kGl<@+nL994*(0WHbv!&@kkaV1}5-z(kP$$o?MhU7ear}ZpZW`edsL^3i+_T zShcWyaGV@B!Zn1}ZP zo@%|{sD9Xfb&!fyyzn7oc@c7RdOOLFXj>tOwi2gG50N^#jaqbWgGa7!H>y{%yTAR^ z9*F3_qi>8|^MDs{u0MY3DBuHZkgYUO$UhGurga3V5dTqfOHeAX!9s}AKs>;+`k&N# zZg*U?f>&%UGfOia`E*5ey@@!V#JXSDwxjpOV{R_C%C$YbW>}{>9 z7x)f$Sc>Gp=A)I*zV^>UpVLFQ{Pr)CQcq;5BB>%OHgQ1?Cw#e zr@I?kKl6#$-6RM;7`i|l#)&`JW(|BYcA0G-!x$wQ71q*mOtraG?%XHugyEfnam!#i zz&cPNVkb0>#L(lJoU#3JMsC ze%@whAv_-gm&E{iZp3&3E^GYp`LZ-tAA~gK6a@ac;^pwAr|XvOc4ErC_}bBFSw?U( zJJ+j|^Zk!bC(iBe@a$`=&s&n+2^N~jRHvMPtH4bVA1;Y!L4Z^*8Hq57u*#+S@736B zc=(vjmR|y{(D3NM)*cvuZ+G$?O|PS~yOX`9aCP-9sW8{^DsZY;hk6zG7+oO&SDHDs z@kZ=wzLcigBf~DbMVIhL0Zt7^1$m@YM};k0gzWm7E7;@p&^-R^2h=y0y!>xMOJ}wp zl;Pla(05{t8i0{xxxV_KOr!yP<+|uVBRU~a7U*ukcT(kp%Xtg@ zc;KHg{)>0;`M2HR?59=tNdq(+EA0=PAhiashDUL%8{uAf5uXoH=p^1Y1Ku!t?WJ0L zFlFRr?UcaLlh4u!21RC7{nO-=@)dV*-^ryy(7}ol1{*2c>)85R9Z9%!57I~^5+?;t zH$UqK=a)Sv!O_+aW<8s5Ri`rxu$08@d^<7Lp3iqAlMP@z#zq01X8rIg6@=sQ+=y_? z`^snc9;n&J;?SXtKWpwNno;K__s}G}6;MM~rao$qb_d3V#3|8eXV_Y<&dv-^btG$S z4gaV5nR{^yUoCKPAN;AhQ3>Z0X_pt#r`G=6j{5TKpMI+UOa0uvey*RhBMqQ_?9kz3 z*wLspZZDQ^!c-jj*r)DriMA2cc-yyu6r&&Nh-tEclB&$2CDOP zX1b4T(&}5&ps#`i7>2GW zLt2G@_kq2r04a8_=hlk}{ldTdz#h>0^G_!i9Ri)KYzB1l-5t=$G>e_UxTK!@l#50d z=4^K&T@|sn8dLq_H4}qVPeW?nk>GJ$PhZy4Iiu z0U^g7TOp06#+1BdMT^m3VUq2m-l6Ig3HNIy2;p(+ABDjD$fKik$MR)?kO(UyW2zHV zvo4?GBKgo&J^n#V)xw5gu~W2#X{c4Q<8*uY6C~}Jq`Y<6oBtZk1lj#7{+$r7tl@4K%h{ z&m1cX&)OZKv$+%1XTWPnIwbx}G`QqDi$x}S>QNI(atf1{G;ao3m0zt7NJ-a|Mv zvR#W;2!P@PL&Z=}u9crwl^VU5&08-Tj;53i1moVB48TS zNA5dZ|F;maplO2E4O3ZDfFUsR<}uxOa6=-M!4tPG(v5xlTbQ zg_iSZ;g=R!7wO@cBXoZL59DGWcMgd9vZt^Ig=0+PmU)lQ4k@RW=HlO-NdX>0)NZ^k0xz@PI77;>o}GKtgKp z!K2>35=2R)eSn5r_UL`IF!c1p^y#(mp$nR`kj6RQ@XM&<*Gt^NmBZFykw%FvZ4qyL ze3;A=+xzzpjoz%pwNg4&XE^^n-ih7nUEbOo(CuwKxHG6(+~5B7WW%Z`xI`HwJat{OIq+dJAFq%F;Tm3Bv!&LugB9h^(ZQ`4(c;8!^Tqr9anj zkKAZ4*7O%K&i-Ez`wn0kbCENpcdsdv-Uaycul zS6K-zt8~aJ*m02iI{w|Y-lEC~w z0Asy>0mjvjCPvu)U%Fl(Y}l5v+wJ8fGP!^G`8(H(;zwm{Pwy|=Jh+lM|AcsHghfk( z?cE~;D3F=9u?-Ql-MWez7L1?LyEdux)LCT%J z7KD4h5#BS6?2E~lBf=T;WpK8)LK%-5%VS4zhMCgw@Dbd|vVE~3e9f0m8Ax3yfm~5l zSPY+>ID#6)3|=GJYCtySt+e|TgcKSShc?c{O^q1|9xXyr1~;q^PyYamG$STjL(5T} zI9It7z#mj8a4SZ@kk8s$fZ?kM5F2^htuVR>+gB3d|_eGB}JdVyP`h z{2R8|>TP(7jcaT@wH~SDD=N0%ozl+`Enfze(ibYhBq|A>a8+3XU(t;p(xo|ZB-%7J z((gK zuN3${%z_&x_I>}GSukDNM}{${KUT(==WBc-`?wKUO~@n`vD7NuHmM@TU=WXk_8&q) z-+w0*WYGOKg{#nQsI8-{mQI6AFJo9t61?$Mg*Rcr)rO9t>K4cC4rb8gI~O64OFFtd zNaC6)X$tN`ng=WslvAkbuu6*j>X}?J(F5Y~GH=5BRlmz4{a|SMPBSBW%9PJ6tF{0| z(!K=yzRs6%=C#e)v#RP!d^?oC4`kO+Sp2p0V-~(0wj&u)e=3*BK{q`pB>lrC*s_U}g6t4EnxXk6uCJPjWp$cx zgt{3>*=4H6%{9&o?hL2%p1BV$&*E8~sOQ2~W($I6Zq&Whl&F%V?~3=|F>pc2*<8C6 z1OonGLlBD!KR5PS`_t=m#OmW@K%o7L^@ZDw1mgWP%ldOt9@ra?m$&;G_-QV;W@q31{m9q^;0oQaJ+4#7B-h zrYGwD!1~%*u{prrL`aV~bGA(1O}E_|m7gg=p8nY;NHweQ;g!X^Y;!?A8fNfR5wXl~ zaW1N7ouJXPPVg?jd@^|rSW;7}Y<59cOb z2of4fGM6K^=)jLvkFI?aYCRM#3(M15>}11KXG37l#28nYV=t)|d(|(~eA3bh|BUteM>;RQA!N~d(N&Qy-0tO)&7;7L6X@(^#F~L6vJ1-B5sC)}nnva}j zO#iP(F#f-y#qOE^9W6HDqMiMRMGzLS2y*FyYurf3Hez$$v6*{sSk6niE6~5If@5e5 z0B*N*v{*9ToUYB1d7H6=wYfC(hfCFd0FP@`q&T?hygATQpRgLKF)`h@okQvIA8hfr zmVd(*+x^dM@mrgJvc;$i|2tdES&!>giBTw1EY8#vmWt;O4_lv*KzYfRcy16qrLOVH zdLg!`c(vDDurqdKr~e7LAeK3~mbD=kMgE<6n+eNnFf-8@a{vV*U_?~pQzv*YyUvmz zv#{7CYgzy75FD?=Z4y%k6VrZHXziCHP*^oVwsU~5GD&A4WS5%I##x#|jD0mRRBNZr zF|`Oq>GvfLkUq*0rQ^4k7D9hnXOYrKlZqYX#8YW0Rc&Ar9Gxpcqlp?2Rh-I^y+fhr zsDdL1BN?Dl#-r^Wc-PSAc~l&n;ELJS=(PouP|Y6R`iAD7tH$_ zR&HFp1%}dnAc6FdR-g)wq_86vj>KZU5G>CvM==nz#K#Ik)a(NSNKJqMQh(YRFBCLQ zho)*6EtM+q92p7uk2(scd@@pNT{$?x4!?`DDRWgP9Ln~YT2E6H4NT7~&tPQYBs2-j zH5%Eo8FnMZ6_MYQMX^d49HV9+{i>i0VLP?n-OH zW-={@+8Q?EeFU_3fw~fwGBTSu)SW1AX2c^&R0Wr{zSpisVj+T&8CkMr|D=!V0qLW| zRykx*Ox5SyvG3E{h(1&Y{#78(2p^t3ukKB<2_MF{bq+~s0t>FBzW3Z3$QMHF-hbg-ebev-bz1;vF z5-UkPC>ck5MVEllKsCxzo_8t8SoaPuFPq7DRfTa+{KH#-uU51(J#Rd_|7LDz#(c`q zygZyNK1sKBi+?#WgiITImu5xR8M!|iNir-jr()P&@ZN&KMDzBOSkDW0#2|CIaM=)6 zaH)(7OC30~8*hB=TfQ`z`HHZ%jQP1eg!>|=g!3)fsJ5pf5@8AYw6Lw7ti4Y$`5clu?qP!V^spKAhS=w(?j!{wogV=iRaiRCFy93A>n?w~0*!G` zrli>tSz`Izgg=CU9i_Hmj6gfI>%dace(s{2(O!{tGDLnhakW?ZX`E5$-#tbNXi0{I z)~{tkx$2d4-TG|~FwWTMHgb>BP~6i9t0rD}vAukJ+%sx8-*48~xc8nv);}OKNqKcW zG?r(Rwe;FF4Wo;&H2pEg(YuoD!wU&#mn1H1d??lXy!L*9^4XjdhsmxC!bFEb{^~bTjn?g zKUSUEqBCgN{T|j#MbAGbsMa5EeD&>~SlUW`aeA-hoO^T9k)5kwfb}Q?axo0?w45c? zy}W|72okU1*EAIScoLJ4mYV?MWt~JJ0b}5 zfB@Mssia%d~vFt7u5$O{$Z=;u+4oYp;`*#H0&1yFXdpwJi#LPT9 zHLlfd36`i;!Wqp&V^ys-4ob)82m42t5pq9Dj6TKGiP&8!I9?_F9;?5@*OzKxGFQTe z&ae>h5V-&K>K^oHr7)^zL46?ZHRf`)F<`>xak(+ay!tS6H(WKq$R&Pu)f34*&(SE0 zslWVQR)|e^`!T-8YUAkg=C$W`aiZh(JmL>^x^?7+`BL=F6+A^a1apk(O3_ z^%CWNJ#9stnFV4-)jj#m+}75hdfSTnm4(~)13r$GF6wFZ_{sGeCs!v|#aCBsK?lO! z$%YcLZQN%r^4?|j=5x)d?>M`!6t!}L5}4!9=3aO9gPrCrj;5ss#=L9lViO)tF$61_ zXfrzJ_-E&?j|A>F(w>@sr?wJ>93qaAje7quyvI2^7tl!78M)kS@4nKF9YQ~Vpe@t8wEuo+!9hAm7(Rl-g*%WL>~gu&$q!9T zm7379($F?6KSIo7>?UL7KCTxjztm>NTbkkiE{m_gcDPUL=@sDh*e!&6bq}`w+~%$8 z+J{*dQm$VMT$*mV=@pw*xWj3qvg68wPsR_2^~tyEjULP_8_o%)S9fIZRW!GpuKdSl z=!KMYvURO-GoC;mwll^u4mo>|l%}0+OOYnR!cW)t z60C7vOfB;p4UpZ9WqQ}zAO0%_Gqp1PSO`qchv{XB2W9>S_fvXykDKOt#E?mlY^7M-z7vpLXk zoW+-M9COq^qo$@#L z1=VDT?#vmNkd2gvMbKj(2(qSU-rBag9S3YbK=n%)q@OFbHvF6E99x; zd3{aUbIYF@pe|9kQ}= zT(fd<6U6V4dPV-aH)Cz2O=}@+YL9-b-(+Rw;Bac@&L~MfJjSzh1<$QB_7}vsr`$qc zUx7u*yA=QOO4-QM@AT7Fz?0A2O8ayi#A@+4a|P@`e03?X3%aZIe)(C?$MI<&`8Eaq zk#1$86Sf_!=M0t4yQ&G=7U7eZ68T&s9b>6i(w^?v2635=<93Lf+UPbqH)ztiO@)m4 zJlx~o`P?W$a*td_ZS$Hf1$OL5eNK=cVJqEm9%uYQHaxJ2ujj9_(^JwsXB|PO^Df`{ zTwWhMzW;dTv!m{ET}pK_7}Dw#c^-%Ep*9v%wTM_`qakuY-j=)!`T=?Xn1zES5#0GB zyIG$b!(;Bsx8K3&GcALgHZ?2zVHZHX`F{~h`K=O(}O0o8!7Gmh@6mj89w}AX}Z4!S1scN_TfTtapgN zllz2`+Hpn4QIgqi_9mu{ned}f1az$C64CE*M zBM!DFm04WnT7H53+V1`;6SZ=(X3co2&Vy?%xYK$1XlMcS_~L z+>o1v9mvx|e%}hDena1M2a!@ttX|f|2hyBB1M$C`T-!g^d&GU3Ja;~7J)52Nz8dqn z={|lsi79_7St0eg+Z(fe*v)vlIg49+yO!~En-6+Aw^F)kPZjs^ezbVFxrKNOl-Z5+FyhAf~mLE0&nIRtLCh zK4|5__O@O>vfcdn_^Hz8aq`%EUT~5}XzHf9ZRF(mBBuR*i^z4`!LQ4NBzzm*47~KmeB7NH3z{OQ)EX}vbiDbz%D-{ zfnf;dAR2X7{hJ&3p!_0kjt}FZ0yUs0X<}fyR`!q3Uvx2(JJL}|^H{;tAuJp6ucTAN%X#U1 zfD+O4M;PC&c_sKa4vvNNt59}O0#GELC?uiRZ)ulf>t2q;C*m)JIY6&gOpz*~l{^b~is9}pQwwZLw zevh60SFkUrS&Wo?dsk&6q3)>;BJiu&Egc3r+IH7yR|76@L^Ihn1JPkmU#`A5fxa;i z6RE(t#3aYyg(qieFvi%VX3G-b*re7AMgQ_W5@k!yl!2c6v?H5>WdfEG9+?YJ=oNFo zsGtE07hpNMN+L6VIZRb|jH-__u{D^Bj&)H*ej%XNMZm-e45Y|$+2|~JBR%3)VIo~v z8Uk%sp@)I>Hm4yp7Mc-WBkKh$C7|t)eIuU3B%p*H=7n2nt50 z?Y#CeJq%$4>22T#(TZ0k5*4o`!cPYKQV|g--Dp0~`K!UlhMVq1PHoY?PvR8SH1VxC zgFDOI=J&=mw7t)#WbxBMCJC1|ttXuyi^Z$JY*1^vlyb)O0$u zPDVm|gk~mKs@ElAnYP{@F+~`9x2cE(omru#nW{M9l8F4>;h%y>5!C;T*U(JWmQjrF z3`2Q@R5*_Qm{$ap2{BY+?H4Fwu3N@t1XLzIuc8j3(=dp>@3{>uZrOx(Kn32`qrl+8 zs3N?h6b<6YMxty9ZdI69sW8#}`5OVZi^>z@`2ncsXPl#Aho=;dX`lb`Vgo zz#s%d!@NXz{#+azP@S$*{qIu3ppk=dkqJqV0wt`Y->4#rG$4>)^cC*$xOVKp3eT3B zRBR-mI4jZy{0wx>w6KYfi;rTac&CerZeXMdhYouWw+ej%<;p=$R)e+m#`t|8XB{&0 zI<&ES#Y-}p17!TH7p#_(4fw*$nXhMg1tiI?_$AYgY#*MC>3m->M#Q>(r>!4`B`JZ2 z4~G3AVFv&5RmTBT^^0LNI$c(T>Nx}FcPglZ0dS|H1|KwG(GgHl2*?aveb+iYy92DL zUwxcAW*_eicM`=QD|rKd!V7~21@9YxxR&Wt?x~Y7t8Io|fQ28afMbn^7pB1Tm55y& zK2$;6dOgSpyBhN+10QD%jBf=61xFry1eAZK2edDgC=|so0>T^W6)|{Nn~CwG77~gR z{hF{)32jO|#QA{z6X+5Dm#|E;-~D6BPLr1C_@O#KK|9FX@GrKUm4@R`_6siym5bjuaz;9HP>31pZP5_ zEY*SnIr`J?eh1`RN$859;+Kz~Pb0P$A8V=4G(LU7eeLG^z8*(0irR~2Yvkl`5bk9e z%FcHc#=a2|eQy^JWNK^Wd@7tXAOBJL$9WK^;1_y%FicLC@S|~;Ma@$1=46O5FYV%q zmt`)*Rs*Z#Zl}1{eiRB?mM?kMD(KgZ%EDD+=j?Ah8hooCgsc^=M>ifW!To8p%&ogq^Ch``1!bldp6F-(VlsLYA#25AqC6W{yR<)wg1%de zkJ!h@-CVbxkTsqD-MYklhW(TqcaWF%>LE?XM+;VqH0MZqW~)HBjWV!L0;x<^9*c9b zc*)-1|4j9XB8j}9|LB=Q4sdhhXwqk2T|$Y3?jL*g%7<@mRzIU_ewna4&3mAlgL_7R zu_On`qXlb*^f;(e3kEMzebh<6Vl1)MM3?CAS{*%=V`?jcT;(TFNo%cg5DIRsn8v3DPg02YV^kYe&N5mP|qXDzJ022k?>}W zrPF*{ky>)kL*zA@9d&(0L+e-^88q0jj?JO>+Zz?W*!JVi$G%^u_Hp=n?|GRYH7aO9 zUoQnV^ctuh)Bm8JO6c2iHZevZjM>)KiBbrr6SkT1ucZ)zC)ZFfR~T>W-tv+vV0g&k zlA@4n!7$g0m^&W9W0keiz*59KDzG9bjy8gSYWq6mwJ_G!ZlChTXc)vS3aR4OG2tIH ztUH!{T;fg~AL!*}zBdi_ySpofqdjCoKYnyg_QLIM1A2qUec?fR(`%G|XIv)=uJSHR zov^Ms2!gs)SItS5xEs;lhXhWZu2gzn@m5xU{Q*~dRE@zNL=mM$>4%oZAI?(>=JDnf zfiO4*tgme45H!6)uJq|9hQDgjnBy&v@TS|usO7=^#^{Xa)6h4D=zaLei#OE!aDy#& zNHpPeiLHwvy2npr&5l|Y&WehTzjFV<`gdtLbwxs-H)OQ-ffC%(h5A(8fWJ&bhIK=N z55_LyEx&xh_&A#SuVYCL8)T``Ff0kW2O*gqr3(6suhbpi^>saSeYW##HoQSvQ+Mpk zU|6YQ`Hz&f^EFYEtdarqn<~!)=Ea7M@1TCslu%Z2^11h(`uv%I+Wx|cw2`90RvXXW zRETMUtLCkiEr-#j!q*PWiyn8nCYN1cZ+@M}?NwAMYl*u_J~)^A8R0oTX#4pXd3-R7 zPRZN0E$w&NH*VI3YUyY;F{`+y*k|Q?*3#q>;o)B{8yfz4j=UA9GI@c%6cjpnsBF!{ zc3N}?5%$~bcIVRQt!354#CDWn;kJ*9yWCQkM>0vt=?x}_Gx(29AOC9bp8Z9sV2)oN z;n$K;!L3LWURYn4%8Qp~1%vbNkX7;vEZ>a&;3{sqs@EN!&mSHq2JDZNyz14Il$H@1 z6Y@s-$!xf+fPKK8C=hSxXKgQ!N9-wr*$;biopRwHVjUjv5(KAIZ0V$ioOH%cAzl;H z&rq?)2*g?a($`AjyPLH*N(sVa@JyV1aEMv+;~cnZUlOjk-|q$^BZ_umfddEGm+8s# zay0&Y(1TPgfAsF~RR0SWJUxz&=$7KHy;~cmdTwZ)B9rY~T3~;?G=^q)wXCgCOHI9% z0kdLc>&}?b$=69BHW0mLJn5hA*>h4WmG!j?QtGYw*LuIuD}L#&1XF63z%^<>AQ;A5 za3FHY;!~;Q>N>2Nmy0SgJ)9Z7nVH)xy($5leTQU1_wUyTcP*`z6bR%0v@JzkmP0{% zxUwPakYGC^VU+_~Ptv#DMj}8^a zVC{@ymo2bLCpqRHW$CHLW*CN8S9LPPsy|FbHhH2pf2{aHLs~?adV+9V33MR0#{x^s zXBydKZY1Vn@7<8mnBt2EHuJ(ZV>3GG1`2bOg_+W-F8OR8?4ry8|I0KA7;OcGX}kB` zBXg@PhPj-YAx>B}%%x^sNLUP?RbgQv@9i&373ZyM0-;saRZ`3r;){`Hbo$-~jq5bZ zeXT0X;Tn#cA1n>7GM?{zY@`|{zfa-fTY7$kI2YTsXv#Gfb1s2Y=HE&`U~w5&tb?yq z>P?#OHL+Q28=4DC7y1=V-l~89L6O5T*cRs@6M>{L&n#1nKiZGN%EzI3V^{BxGseBA z2ZF9|NzZMUo_g%8jBULC`|`b`XZtnJ$a%v}^D}CU%u8MMQz80+g~%sZ(3xYWDbPK= zLsQCzZR+Q8@9Oj%EW-dVHJs(E{0za@K_7p3Lje8Yb%B;1bb)cuo$)z?l~dExAm2Ou zAu+@(#y3K;=Wg|Qh&%8W4s9Z)Z97T5evpwVJ$GQprwIN&$DZEok4gn5WSC@1eyWOF z0>!%x)sn&sVWXA6N_47ss`Z*lvA0G0*A*x2z8Xv zV#!a$KxQ)Q?ww07QTiyfx%vS9UF*fLNDRMzoefVWe48|5G6L+fw7hJVRDWDJ{^FC+ zzG+)}uKf&f5l&(@*ZdV)xOdmqa6Jq-awwK^e=s+>d)U%~jm&;3$wEeJ4uCr0=}{z2 z&0o#0#^LzZ5BEfuB3`h0khhnV-@GyMTIWyK*u-P2>BNBG6F*EDr)AV-rjf|y%%RCkpH;v9u#&VHYn0_m*nfLs7=9Iap^|Ag(5I3w>ym} zj~ws(iK;4cmft7;#L!IWMsH$kxAd=rGY zKB4FEL7YvvaPuonC_cRCD|ci}Qx#sT6@^mZALae3F@LY;f8gqL$V!NziC7}U>tn`F zJ|{!PzogV0nbQVQC!X(f-dQ`)^?uT5nAAw5vU-xSSZYkJjMwT{nDF8uA5jp>m+MTc zs*=6W=^s%#ZH}jUWjwof{^RJGhkAi}b5(z;dbquM{9TK=- z52DV)v9YfVwJ_0-Kc-e-#vPR*)8G?f$iucg`e<#O%~^{H}1tgvF$|c z%2#xBWA$O*LGl$z?qx|Cf+tNeFaA3nzz!X#Zwu=Z%!vUidGWD;Q5Q< zp?18JkpVn<6PsLnOa(dLxA(6Qn8f_N9Y~*!Ym)QIr4A5Bx6=n6^kc>79MW2S4Av|6 zxds6j_rl(Q7ZZ$^=<3(bhERb=1BASm(JN=bn_6FA^tcH^H3)aL*0_1E0HdBkTF#{i z$Sg1j4JBgY!|}Z5!b|>0`J|LZ#g?)DTup|I=k9BJG;U%3f>;=g(l(SY?>`w#!JsIxZj3F{Iji{ z<02VEnKok|t6R)VhebPT1u6twq8t$7b&!FwyZS*@2S7EDdIFjpF>Sbt^prJ>t;8C_HysVxBI@ejYM!33SrAqivVLNm~XDh%n~qvN-j zqVxW`{x7)?o@c@zoW7>`t!rlb{Y=mP_&nds9~32QgKh7#li4v*Och^lw{C`(NDWTy|Zzt=y-F2tE1~D?ugY|qqK;HtL}dCGpdLTe%}%Px$$L? zFAlsgVno?Wpf7(I6O{1)N)AYfc&~n8w2ZTY&eN8$?_A2B~`ZYzG&Fh z^6Mp`vGbV);Juyeb8q>g17F^(*8`v3G_qE`xOC%-wb0^HD;|3DucbRbA57_50J}LT ztnmUW-8b0H-nusAdq^=P^Z6O-k^7#ladl+tpuxSsszuwq5Mv*-xesx{g>k1|15uU?J$~={2zLo~Di;-z742HvOThSWK zz@Z6|WiuI`9*gnqmR7JP%J_0E%~bd+m`bu@ubiqO1gv2}g@bKdF&g(Gby7EldD1v? z-@8PG#bQ%^@9!GjqUtjR60GJ$RqaAqG;UiLo(vy4$;Q~UO{Z!MP=?ZgB}QkhF2=Q$ z8N7N$%)Wa&ZdUI<{1IdUgj9;O2SVjy+Ev<4A7}$ZR4rJrUcUa;EOdgZAzms{P{`k` zk@;fxK8QlUPO_+Z>Ez_H*zb}`;$DP-cLT-{*i=Iq_nGUEz#>?*Dk=Cqv6pcC9(-pg zqE9?T2ZyMPYzQZJWQvY6_E>Rk9~6f#V4lfs=$v2_VcCrROahVgFtHQb8zpBpJcR2H z2@;#Of$KC*Bc2xN!^=6DH_K1~m1^2*_xNnC+=QMQ<|aZ-A96KKv(gd`?|AKA$BkkJ z^cDp1!1TMY=E7IJ@czfYo|_r0On5VsgNO=hI5qWl*e)_# zoXaE_6VLCUGqNouUmK;94bl>SG4CaKM!0kDHO%JvH-N0OF==aO1*1rw8WYSk+6|I@ z426NsfCTf}T-af>+9dBKXN5MazQIyfgDY1$f)|jJPsxKHVy=3@s~v+hIJZ<~xH=@K z%~{LETNaCzAR$KiU)dW&FEIp8!0_;-KK;F?} zvNJ#~rtoCeg(|H?ig9X_i6cq;i_1}fz0JphV6yAv9k#0yi5%BtHS{bVH?BeB+tq39 z%Tu-YTT`34-NqPZKB4=eUamX~y^joAUbBz}h50XTIR9>Ov}ew4E!{3Xwb>+dVZ>d_ zWAn=o^Z(?q@AOp;`?AVm7gsrK|JcrVX# z4vY9Fhc&Kp*xKB?Hy-|j!&1!u$zd9Bq96TW(|_bJW=&7dDu;ax1~|-Dvgg7-Ic)GU zz+uP!ox>90@BQrP%^v^8VR`>OhdKO{!}R~1!wOe9?2p!%!vDZwDgQl(r2!m9`af~l z>_0fn8{n|R|JNJ_1~@G0-#DykFxUTogu_^nx10apa9GVMhsFJmaaj8QJ%`N}Z?gDb z$6+D=RX7a1_T%$wk5|62^*lU0hSjbfd+;)N^4jB>Zyy)up8NisrQ`rheN&Md>S4Sd zJoqkeJr~)$yI4L)_#wqD7@4eqZ$G!lnnmjQc|Rc^6V=vrVDv4z5G^wxwqpDT$fHLH zKWeL-p76cvwwHgM;q^C?sKf6em)l&Rm&0OLf#*yHrr5W-)RKMxQ%f&>l(TuO?M@8< zS~?M0x}u4k8BixeT`$nlo%V@tGwW1bbm*&$UZY89MuatSb%|p5aD~4Zp*9Nn`1px5 zF$PmnjO7Y1lktOXXz0?!tsf2G(NmAl$Y3pU>2Q7Ts)Ukho5soQlQ|5SSD7w02dUrj zIUkeToK%jb+L&OX6|td0U z)%6$|tkvITcS14W*HZ4r3pL>udJ|7vDz~aM!JE zj1Nyp>Op>p@@GCXelJ?VFWXf<%6!lAo+JySR#A+Mm>Zy&hv=5rd#i81A=K9)#Us}B zQzWfSf>VFBW5HhWb?_bTmL1#2qqlD#H~q8oFOPVzBfnYwh3e*gJ%IrCCo#H$qPFj2 zupog7Bxlh$L-GfQbXMLNVf0ksLgy<$>#`gsS^ald10WxSx=!yJmo7&Rw{&?0tlfx2 zKnKK1Oc_rD0L5e+%lPt3KOWwK9lsLi$i^p&g+mjt)`XAA59lQlL1pA=@o<3BV>Tdf@1ShgczUQgSBpsKU3VZ{^u$@LLpZ)Jp7|%J z7leLVq7?~fT2njQy`oiULIpIO)`}+1``SFW7vP`jy+hN6VX*FEu0ROr07Tx~>-(#s zjw?>-SX@``ePx{e$>Jpu?@2FG-s-3*Um=t+p?!cNOrm(o6(vvVQGO3`2jp;jq|ogy z4*>}z%!wU&_cOVQXZZI1;e?o1TcgJhHa2GGyjS^_i>A@xI1%dkC~wW1bw`m#^ky0C z9t!UrkC}URv@%6t_$4Q!Nx80zrO-;PMv1?);&q%R8CVx&l9rVhqW5KjW(4Qkhr5Qz z=+l^TZ51PXXYZ4Y1MB^%by*!CZw_rxG%6Uo@C;Me8e67co0o^DQg5dkdF8~MkLO_m z0*39T3?!eMPTs;(@6XD4B^)a7V&tvp?qhV*o(v*Z;FjnQlH%xY$Ls>ETt3OeAZhL6 z+$i4XBn`h9O+CYtH;@RUDTMV|oo|#qrz?x^S!8U|tIbZ$gSdB1xOWB5uE;27$?xm? zb4R1r#!+a4s&xk{hBEZTCI0A(^m@~|O}Qv)CQfje}XFDvQYX_Dum_2UW+QKcg!c@8FYIlVN}P{!ZKvC~QZw zn;?iWmSo0lhF>iGc;ZgB(Yvyu(?X<~y5A_ZR%+_FezWAe+?rnH@6Lq6c%ymq^-biL zTTUvCmpO_9e%&RsKmMX4rY3ap! z5)?R?&X^+)yG7+FZ*G2SuhF**`r6I(enVTrWjy%`+|myxD07vW$1;m$b!aw+J8s_B zZ1F7S9VnL}S|HIF8>{PSe(j{KDBM!FTcKsB=9>6P^}RB%-M(R^fK&{E-Ml+taRI0C z$F>^L*_o4LUy8sdZ}zGpleQ-RVEAp`9QOm`xA_jnEMeyJ=dXI#(}h6CmdgmHincGj zuj}of!FsRPdM$3*f&R9vFj{&B{D-~&eH40GVb14_x@n_&mGnIE^zhnWH{TLG_z|yGp|n3%`GjnX z_b!N8m*sC{E=NTLra+`a(BMF(kuj|~F%+{!NFClC?$ibSP~!Bs3+feaPYg}ie04G| zs>ufw;*&S5gbMLd_Xcud z47*YSbE@F;Qf+0aAn#B+_-Ac?f7I4;YUXI{i^2nx%VUV(`i2)Pp*2^E7psa>5$ za+#u1;jMA6Jwkm8BrL&iGZd{92tc89IDYrqatE_!arNY*%U*pU1awYEYyV&T& zNuSMp)Yhrv@`0q^0)eM^&2}%cSQ6^lR|Z4pN`ptTIBrNl6vK_@f6QJL#Rlb9MKOPI z?(GMFC>CGEaTIV?w0^m9z>Bks``zX~9DDHL!(+wHU-SHJ%ROB8=lfr(9afn87mxjm z$Nm#_vHvnWMm=KHlr%s6D}0ghaAkTvX=U2T2fDiD|IG69HbR$W8iHq-OPVsp%TO87(uGeVeK0Gbs=)0y^3El6-aE78tllcO$-k&HX=o zRRG$11kGWDwA*4#(cU$@!f0#B=FCQ>=kOo;qJULi zIR4(Oja@#b+2fp#`vV1J0qx7Z*x^}6Dq~taZw_bwLlYB_%R<lJXHtmdADS4T^(AL8+;#$;WPh^0NOFzj z6uyZyS32>Rpt?~|#igMqDVi?Q6tK?JGt%%30Auyp;smNoe~V+T@54@&e&eft2%)tF z6~TEAo|H+EaVaZQ*nE6eP-`ktznrD=k!9mb=O`$3X32`#sy7ldWGhwrb*5aj3>3Ar z4A##$Wd62M7P3n^x^DItN5*sHpKXVD?itfO34#2riGhp}%3oD@FOo>k#EbB!VonO4 zWC^$sdCy?0nwV&k@#g^LCRV#;>W7+?;q8RaQ7C0W)HTI$21F?4CH3Fm^rxj?lI3+2 z^tf!c5&9W=7tcs@BwvjK;Th>$%uu6`FGu*=qu0q@kXJ&n&If_e2L%j6?_Y7Zy5-%Nn^g3TZaI-5 zs)TPheF^2Zt_()U#DxW%uDM9`cBO0YDB0}z(U8nkvmc1`!{3h`9=xWwE&Scw^`|Fq zXJQmqJ&O6VxObe()8ti;qT9cD6g2^l;(oL7AMt;jpJ5kNta=n1(S#os39BB(jmZ!r zUfQ<@uNd+D|J#sQzaITxDE9v=6#Lj0okF(>MUC;|ij^>@m=_~BKR(RZ>-$u_^yw=z zp885(+qJm^g!Odqj@Af$n1G3;k#;Rx zW@_H@v@hV9%))8Xh8h7Ns&#y!9bTVCjkt7D=Y^Z!x^&mWML_|BN)^I4E~HE+7m@H+bLd`Ta7V(qfRvZ0!bAZ9*y_fdz0V}_{D z_8=M^gTu!}#n16`qVFjY=TD~TM%LQz>Fu|9BZGc569>&xr4n{R9|tNr)@MAedl3a16< zz=$7>ycATy;iJf!#3f?m83&%F4TV0Sc)N(rk()4qe2%5zxtIB%2ll%aM)YN4g}(6Q z61hjHgcGQ4vblMza0?ClL)6Ql*y8zO{a8GnD_bPK`;>ji!2xR2#VC~fMbxO!4~;!v zBxh0nf~Z|pv{O9ODj?5f$$?`@Yc-HqJGsKsSRMOw555kcxz3jR{z*@fBQ`aDkREUD zQh!=k1TM7D>c(1j;C-TOG9uPkkQibxT_k5ZwxNL)PXU7!rlg{VQ=_ZqaH-5|EW#qs z{*7LK?dGEI2T7qkG-Y3FwF9Neoz8`;OE~7EEUzE~i=31s#qR=XL!%V8LA=Eq7Cwcj zKu%2968Bs9XDLSa(c+R6R5h?jM+Cq=H5KuJqTUVxXKfHj=P9nj)`C%|Tpy+fa!B$#cKl-AYK^yt9-K7gw->&_F_YS;!;3W&aug8N^ z&G7bSL(tDy92w=P}B@kKGY2bu+)f*UlvzRI2BES+j9N%ITI!SuySBnnuSy#*!9Tn!ZigGlT15RVr zhTRVIEib;-ULCiL61m`uiSd=RD8|liw@=~43@?o<5hqz^R>7@ZZ`*OXl9cj=cN^hP zM|IV;s4AQeq~U?fXx3jt7t}M^!3~y^ zibjPYPn-ERU5svdFdDb%1zsRG8W4-$IaESO-31~IgSa&tkoES2h5SSx?FnxCJ4@#x ztwy9Q6{K0L%H}|o0UmV9C{JX=JvRs+Kz(<>>hsYlKIzqU?fCCb5p>NvMXIqJwe%Q> za<2z*k~ZC^j-?5OPiS`WY>s}b@}VlcB9&4Jf;n%$L%5Yd;ihsJ0T1uS{EySp7()>Qm*=6t2(zMH$&5eyFa*Z$e`fXd?Z zBq`eZ*ZW)c8=90UAG!b=GtlA`?kWHBGHf0OO#aT~FqXe=CcF)Ey;0Lx)py}N(9;L2}6<{*DHo^=ec zBl?z$iXCnUhfaQ@YUJZAs>4aM?@OTr~` zUprLNOB|j2MB*tX#*kNQ@DH<*npVyayZ2vuYfAi%9Zz2CD16)130H6|J11|SBU|LV zi46%WH+_lE9k||V9n`z=*oEWEFT-k&uU-URzvdCO)Qre?)k`eQaJjK;?x~G|0iAoI zQC_4*RVy##P>ur0arhl)pkMlr;K$kzu#J5W6`;{=AWEyWdNkc59N4Pdf<8I0>~G5zg#X@6GX zY@xn0N;MmXWn<3YgX<$F5NcFttM=%=d!;=g1_eG=zSiRGXm2by>O@`+Mkx%62LoS$ zWTNlIRB$PyI|QNR3(P;^KAvoi0%0r1E>Y?M0OME|ATt;Vw{P2g__sG{s#| zpoq*Mx#}?wx|5Y8aSJTVw~K?BkN<^Y|3a~UN3j<<3Aj@A&4A&Quc&?<@NSa(Z^^i? zvRM2$S5UOQYL4xg*sy==3B?6#pn!;A$r#mA-RN_nlb6-&(icf2-=BmlZ=j?{$FmV)yIC znxq|N9hbmw$G^3E2JB`E4$nJ125k}~?$o({f9j8+l11|lD8VBaPz~)!RplcaPSMWz z_R4!dvLR8*?>Y{(J4JV#$d*^uE)$X)L-89wQp;$iSN4_DjSeNsCUcfa!NO$7e2(%| zEj(d+ZaRl!7opp4OiFeszc38B-tvnH0&>DgKnsGk*l z-|I?Cr|RkWFIW9h`vT80j8?}sGnOy~PxLA^h2Hk`U z)`-fIbE-?+FIA0LmAY~OsVkVgKhO`pIfB6b!pUt##KUEaL2Nly0tBQHl_U4&io(6i z7YW2wbDR_~$Ki_ff9A(B26}x)kpXI2=7Pjg#taf|fBk;QE)S5UdY(xSZxqZ)-m81B@Jrqpgv z7b0N3aj^OZ^j9-8L5zpgi?rr6<;-?}vkc^}Y_skAgrz+r~cl4J2>77t}>N~ zo@}U-mPCVw@_V?S{IHN9ibrHG)abG73E8b(A_{!2P!{rlaG32np|0g%4tZT z)%LeU4fM3e6o9MqRhx{Is`G(l)E=8Y0qb=K8J2F&tEmCI$88ij)>sx%kA?W?Z^*IT zTr3A&;d-~je0nE0-N|niOX5tK%jG#w{SoZjLtw4qG=7QeO-;c$m;v4h2l6 zST+30LO`sbcBozNmTiN=50NxsVaD{ugWXiK$sH@ZPGOJE>9@8TCe1zdm$DsT9El8YUnUoqR8XXFhwO7ByS!?X%V(t_dF0gy4 z>|g|MdeAKy>@R4+E8iPd)+%GHP$* zEk4GcSroLv^Mvlu_a~5VSjZs*@BuIKv(Y#VTWik#GqjuJQ#iKgO0$mfs^|OlLq`L> zhPy2x*&wu1=@yI0P6Wuk?407TiynSt(l!QS_dV^-$=uz0smiu0gc>C}3#F5s8)it5+j3RB$7Uw@s^P%N2dY$|9-!6^VA^HVxy z%;TSamrG#&=O*^D zkE0_~D4t3ZP@;0&?x3~H1_KA+A^DXZM-pRhx>y8FJ+IUc$^b5cA@AAbUD3N&nrcrG zJkc}z2wG!HOsMTa*>!kM^bH^*RpyhzY7*IZXH4m zgeq<9REd^)AEUF_i$~DIMlx_22#c+!08x#%=YSGnLu0vh-VbIO1@A%8X-tA9?XPR= zhn^54hMv*R2=BU03)_0QnO{tMfZ~!5z314qqM6o-uzX!43_H8y9_n_Ks+vy(1lzB;2}!^*rC2odx;`-^788>k)oW z1}yr9o-@xVISRCQ2uVO3jkhrFfT55)fIbw_d*tbNH=DbS&VHx(|-Q^SdVAj1UDk%NJH^q;p3;j^OqT!E58;fPOE8qk`*mq z_NoMOv0#7sbZnf%(GSlTlN0clJRV)-zdidcvlHRC&kRUfHJR~7t_zy-Keu?uB=_82^v&QfmJ&*3UtNl?K7 zpN61@ak(yV^cp6|MrXB`!n8tk<@EspjavNIwzc+6yRVV3cP;x1%DBjHWccV@JOYcSmBXwxz-KTkiVk3%!pjI{!a|v7gp+ltAD@{VOf@ zzj0d3HWDvP>v+v2BqpAE?TJtPNNo^wT)TGnXzy3C#4O|!)00Q-4skFz`qSk|$`8!@ zL+4DN{BY$(#-ry44VR6H^RB&GaWS>R7#`E^q8 zc(}cPp1Pnk8*#rmfPWNYyehC85@}#%D-LQje6Gav=FNaC`(-C`0>n*<)olrrN-IzC zt*S!)QjE3_(#vwVo-H&oMw%}X*4Wbc;$N|hb8_fdri!h!tgeq884Ji(UHB8lVrNjF z4M8IzE=%sFtl5|@5IFLKLv86iIcO}P3Z>q1nZ8<=Er4oF6|wb*j`aT=zTj#aox(QAkc5IA zMh4E+7u4Hx%(JaOXT#tYbQ#$)g7-bNO2364S;W@SR=;8UceYNE9(5oEebau ze52r#OC$IxY-%wkK11y4m+_=CjkNgATr5- z%MZignA@<6gHCcLIVzGpH4=)kQ+<1yk1@Wwno>j^+UITkF4qAK;8lC6Uykh^46e1q zo(n#x22q*1f&rwcg^~!+tNfc@XdNGo)NN|EeY70|pRK=o+vEOO zE--Ga)!?2(U$1hmY@p>C-*W*<`NSHBT}mW7x>poy;T_-#EDlyaI`UB9G6Lb&j6SH3 z)w_kdC^)}(ZG+FRzC7tzIZfM&Xhq7AeZ~TFRYs|SH223skz6G2b%b~zL~MGJgSr75 zi+=iME1@O$4@#MG}NEfzn4iT8>J;7>_-XfpOmP#>zc@}^IS8?37n2upTyuSB49e` zKQ#=%Xw_29ot+jm8uZYZ6>gkD?6MQ!cLc5(SG7#w@9|sij0r?<>|Oh4y@tK4$}kJP zw0C+AiLT81eEay?jHYVD&tK|_g};oJj+q_F{Pl48D6oKv1{P44hqn{R;<%fLGqppt zstLu)y~pnkf@g~4#lCC$4t_CR;s8_rKri9S(zV%@E!17Hw&CivqnKY^%-s>%n}v?eoO1&&*y$ul5BDUP-c>W#3t+6{jx1N5AOc?8(4Lxi1!3 zSLQ&H0xJVY()RQ9t`~E3bWnFs4lq}-rqn3p+k@=&iqPQN&ahqKNxgNfIsT6 zI*cUY16WO!*!phJ9vM#hBiY9CQ#wee=_?MSU$#(NN_eM|z133(2&xQ5=6Ucz6bl3h zW71=7ETBn|0W>MLv{ok$G`Z2Yr;+KpQ91A@KR*xII|MvyaR@h_vQl)Lnw}MGOMf`K z8sgiZCb;dr=iYgY=9wYKkSw)%2l%Q`YMg z(yo<|{klo&p*ufsmCTU+wBCC??|n)ZascWP z+|%T}FujUaJS5Xu@;=q_bd8T@LDY$@9Qn3feLyNx!x9YZRzVPqi4OM4k!q)qns$Ca zrl+a7>51u}N%C#5e#Qmn@5XPKrNu>Y<>XvjO_kA)wul(mY6#o?#`=1Pp zt^B@z$m({oVYliQEbQm)+b>?pdk0Ly=2vFihPgXS{1gYxL-*zVdGdz_R16LkpM3uH z7#UG-CwJ4mjq$vfq`un^q4{jbH@^XAoj2{dDXax5)&--jMn(uA{o>4&{;(1ggP^aS zPL|=dknDprjsjSmr6pz?f7`uJ)^c#v)hWpFumwVEtyQ0ZBB=`TBS%K- z31Dj68zcW7sQREO2(-!0!_!ysz46NL@kXvu2-rt5W^)7JX?zOvL+};wI#VV(UgyP> z!kI;|b~q2h`6z&D7Q0c&i8J68D^ij{>e$xFW(m5FD@P$6-lnmqHdE3Y9m<)s_O&C_dv4jOWyrQ z=T~b!_WNfoI5hs)mch7QD@mO%Zs7D7AT)=FN1HZeaFj+iUwQ;DgPXKu7q=JdJUqepf(?Iu(9-Ro2lFwtlw(>wvw?J$a%nm#^3bu`-A z>Pm0Nmlb--In!HCwq?_bl;2n>>QvBJ$_Y#=#AJl0tZAC~st#{TZK2Q>peE`=W+wT* z)}zp`%T*jI+CO7?zQ5zdU5Uk)vLYRo9+hPvSw>t1x1UT2;_bTMK(b6IgLUU3Kl7OW z<)cMFvg}C5pH9fISSq5z>P;5^poJeoK840p(0-n+H2}JP@MUKy)+jcJI41t}?*Sw3}nh+9``D!D_4V!@9(TnQYLTe}URn?fb$MzyhT zuATcOr=M?fjm%r=4}d!Az4cT*A?LsLrQutF{~M>|SmNzYCW*twe&4~{{vl!@k9po4 za(wSeZfx_{H)Pa@j-L3-&Ry>Y)|H>m(kthNpMG|PfOrvkHpS$|3|QG)u6g2(-6Q!8 zPN1tDk;$C0A;;?{Z2p`&VpFmRC}`B(D$1QTn;6uCX$|HCgN+h-|AHe*oR()TXW`?# zTrGKou|0#z-fZN1Ww3xmusq!9cq8D7zctYE@xVzuA=_^kEX-3C6m;vM&;{KwVxZ+? zV1w~?`Qo+J4!sT~joW>;QU7lFDDbPw8g}1}df+-DFsay2Z9Hg{Z(@C%{L=HRQf%K@ zhUphI?AZNxI*+LDmb1=!c2GIKc&=Jb2`QT z!L_Yj>-H;b8A&kufVWKyADaQ?J=5YSsF(cMZX2yn64Gf2y%TysrHK%F5_%7ah>98z5EL*J1r;SEp-M*q zDG~$~1(ha6Q3=u!K}4mAp$du!7DPpuf4uK=o^#H8n0MyPtaaX5_jg!^m~!oF@BQ1V ztLwbZ8?txbzWLfU@Ro_(_PAGFS`A!`9XdZ*Fj6+R?9-IV24k^&Gm;lqFBaGy+GinlkKy{)BPxu{Q+}` zVh@qzE;_8kP>-6)UgK8F5T=uzk%K2onr0}^1oiNRvEC4dBPl&&$un4X$4L-hf$iAS z^gu<}PT!8t{$|{28MK-v<=Os#4EjiiKxFakxKYg~7Qgai!0?4QJSD4q^P8{A%dgcL z7qTYyAab9iJco4rV8H}*QlU|8pw)>^FV1tS8JB&Fh zfD1?La=a8~gj2#GwBUw7qH)AVjWX{?W?#NR^E89zB|)7`XFq zx%GB(9O{`1qS767_^Md-M@dJO*Fl%E?9^_hI%LOTW6AB_0B`Z!weftu;F9L~)y-uA zKdKF^#Q~Q0RDb7*KrNknlq(^c$vbbQ9($%j*opJiBK%%01~4CuH!YTfsB%~0*vEdX zs=nzuXQ?R}`{iw6>iV_|gXKZ3VP1pY6dj3S8rV{F%El=(M4_gYe=l?QZOR_aCM%7R z!2{U-9lyHNndTL*k;cT1IsXNSwH5+Y%yF3ni%)WO8`TDSAE107B^DAU>8tL?%nSeM zwu>!T3ZbSK3Oa&_0_?N#0RGZ%%RCGb9j{_ilOMA5<48bzXvk1>jJ|rft!OD$l%jYO z!8IUloLVOv-1vs2?$CFtdA&qwWuYRwqOT^bm#^Wyyo?*NPeySGp%-{aoXA8XJoj=^gNVkl%kpMC|V zE&0YPa8R8jsqkE2`cdswiIy)BcdQ^Xhqv}TI`6np>~vj_fwsBp*5fu8hq%p)X|RPz zC(-Ua8OMU7=0lz#g+o*2I$rGaKcL23T~HnGZBSMSJU(<(4kl@bJL@&b5R#3lHc_Y+ zFefxW4|xg;m{=QiF}`zX#13EVanWmz0sPW~6yXeKT;vD7pG27|8H3|HyAuo;*}s4= zqjWT(9jQHiaD`=ZFGP_4ayDAXW+9FRaTT0@o6(e>{_Xh-R>aJbAG}B*CrVggRWM-T|PFjN9I#YC3<}ICCTQ`lM z6T*Of25N0pNCZXfEMU{VS_Rhl2G&ePlV_p{c;GDwt53V@I{p;5@AahaW$GEzz|g)f z#_E!47bEKYvrApPX=ULvADDwZ(#~fDhTC6+nN~DCvt>3qm z1&1rPkh9{>7o@A=c`q&|Io z%Rm5MFQ+fYl`2gDcMZvnA!uT{V=V~tEIa)Jlk=K~Zjmp9^IRtknW>utfkx*9Oet1j zkPU~Jj}!~xVG1ndltE0Z_~DJA&2BZOx%rJu^t1KW@X12VaVuc7*g{F3gm$gQf zE-9ifuIk>(b@bd}Xj0g^d517QWpnLY<(ckRY)NOto)N=1q!++AVd#El|Ezw27UDz-l7cTO65JNLCPnM{@`?ULT7e z1igpOa)_~e>^L$&T>MNFFvoKL9;^bve8kOM=gMuJSkR;0?7SfsNi}#v`LL7!R6jrCc|s)1=bUGTXz`0F=PEmRlD23mFFOnbG|PD8 zmTci>)~EG`W;aAobUj2L~+k57&MF6is zrhZ9>_!(*jqMB%Cpz~Gj6pIJpRcz#}>DJzL81=msIaxX9%G_gDg!d*l_woD6lZW6H~3MfbSRPV+hqd zb`?b!nRB*Agr6sPumq5g7{556k0clbhmWIQ7F#Vj=VZ?26*{TtPW=v*)`{xL*om6F z)C_`YYH9y4JaGsF+x+ReiGc4vA6ujF`M1WQ$PIx|8&{2T*Pfx0!wc`eWbFOvjIaJ& z8AH+{cSED5;s&49IG?Vq-T!O)?w5A};$zF(UGcoYhu;zkBinBRADb$-IG?-YI`w-G z0|QnpNMr7;I?#1C@^-08jbCD|Jd4E>|C>1FwU4|mxC};3CZ8^pJ1X^7(HX&_3Zkm+ zqfoK%-`3>Ja@$^{)AcsKd8+}*>J?oLzrD-BNODEfON4bP3OB+7IA{}4(ay^J2pMvw z>|MY?E27h_dX;dp+@t5Dd~Bby zk%m)D8;7w54Z9R{2wK#fjqI%NMFI*1A9pskf~9k4kMLm&v&fp6J35Xg0e-d};nt6@ zf<=32VEmMM(F*(fkOc`TzMa4vz_uAHdS?ejtH|N(57%f5wuxRI1l_n0rC2V!> z173q&_oi$jTdRs2YE(y}A3M^}F38fpTNm^K&&tflEJuoD$VN^ALPmQn?bD3*byiM_q+<8_cx>9oz0IHW*6ndRBzRvD@6WOw08jq`2y<3rp$ zqpnN!5AfGik)^B9z$2e+>7*mIUMoPJG4fK67sD?jP#{7;^+W#iJo2ABqq2LTVuR~} zwpBsH1?0OTbmc+hOpl{d=Gx$O$i6RXUJk#knd_gT6YB<}W2ffHVS~uc4ozP=yER(b zD(T|FxDNf+%?g4OJ`vLEb&Y#=kn3Nql=wVUWGP{|zsD%aN%~TBOzYKe2`tMF2i1I$ zUVT-T=9K4tlFX->5T{hqX}f;1$g^LDlgTp#b$XR5Nx0y}p+Qa?i?n$Je_vq+iiv&yhU;McW; zq%)qHq&}V{@o9DDo}aj@_90Uxl%d9hf)YV$oqBnR1c8}ig3pHkq`J69vB}Pu)Dj-( z$I2a!U1fPR@VMpKTvB%Gqh&Xa^1A(IO?hj%*;Nx%;kHS6erLx$p+f1`ugR3s)gH$# zZ|-b_NA$H;JWMYWN;v~upn_k{u1nJ=pc57j=5Iq{fJ#Pj?mSA86r zM%1tr? zGUU;cU`${ITY~_own5Jxj&P1rqg+pGOJRR6#`vb1a0XN$Ml_>kmVX7Wxqr`5EU zO`V~_pp^}DM{lUmE-`ZW0XrqHI<*|#u8ZVZ^s!vcnQ*d|653lZ6auxWNduZ?JuLc8 zSJ3SYcq{%)`XFwF4wIDUqRrmEbHzF zXRGeAZ7B6-y*XP#Cd&S{rHXn)is}=H0ywG;yBOdtHUtra=^|~5SKE|CymwBwaCwVE zes-nD4LY=rbVxxxq{Na{g>R(-kEK6Grj&L-gjcMn80~ot{K#q>Wn4Gc?#>HM+W1Eh92p{#3dRb&o z3jlN*?dOaMV4#KWj-7bHF*fC8y!<@lct7J;f``uL{9{9@)xF}S_Ah8N)vV@LidF>( znt*Fd%g2tJ`^HJ#LEPBSzU4tpcCRjNBorK2%Wy@UBi|K?q^I=t4I)##(O_eCaSL>N zi-;(qf&{Kj4l+t}O}?vyS2Z!sFRwRv&C*L6Va?c%S2 zk{|C^3t5!8y+(JrF~*7cGic&{=i_m(T_pVK@P2HFxMg6SP(N_s-=uR7{7ICV5086a zpFlp$=^6Gxw=L|_G`eMqE?I)_UsKz&OHN9})Q|y$O zs(c}N;g!T!hJlx1@T-7Kfe@JL_Z%9WTPhPd)c$}&j5;F*$;g&^;IfIb7Xm^|p)G3fhbmDRaQqnb8h1OjCa zmo!{E=Gfzt!KqgTs$xgKt)4qPCI^8-7D)!Z)Cyb5yu1o=shoooe)h?918%j_cALi zP8@MC#p_83CD6JBBhSK1b@Vi9x(EhL+Zz+T%K3T)P)cC_WNS3&BkV8tYOmGclJ2-C zr!%)&!&CNHLJAp-0V@Gd1n9Rdus5kEw=@ij;w$R(4kM@!K)qGA2WhU3%QwSmmmK~u>Um)CLJf} za#S+-Tmt*fWgnd}fO%gv@ILEmgmFtUfLUCPSBk{qwYZQ)7UTB<>juu%reGp1Y=4Cv zoc|*WA_t-K8Pa*3-;d9V?=OE6MJ)*a>LqA!#yk7?#<#BER#^6Y-3Uo&83Qt?=z=9o zwoCWMf&RE)wD9VwgT(xPwO*pbsU|_xP#m_N%n(FW@^M`-g7Ju}agsTMAwTm4imw15 zi!X>sJb{Jzb8i#e4V(F%k|B`%&^9tov4*nmDyNEEw*7f#Knq5ewH@Q1Y^dp&>S9$_ zYxO#Eg-A39#3IlqtNy!B27LJ4*yroB5_f7bKSB#2dng=G8p}l0E3%`g3Z|@sT;RPr zS*K2Evo51YJ(^RhWrbRPMBts*`VnTsexPUAO6ZtV-J0(Q$R3BcdK9K;)-RR@*1qmO z^w>h`)TtkuWK5&u&tl zqbV{sqh7@-zfc=5%%rO!;g@oc$@! zp{z>I$?q@+y!Ii31N}t#j6*{$+cySf%kS4O^BHwdj+L9-Oq0mR`DX6=ekCllq76yi zF$nKEbxLKJWXr=k%^+z}xMaBhF-}>l4v2&L|DK+53_GygpLdV{rESz?<0lHA<+I$lV`rIaYp5D zY0Ta=WDW$PzBZ`*R>Y;cdVLTD*_ZmplYZH_O|PxoT}A%*&M$IiGv?g6AJ4B$cdaYr z4p@}9FU~CnifwHk>~8sTqK$d!@SKPMO0<<(NTD0{p6gURdsk1VcnbFYgYq)sxiL ztJ`NpQh#YRUBe=-rtt&Bi9c`;FyMvCMZQsA#^={D_@4pXcs3_Hb4K}rn2fFC!B+Zw zmO-8J{v>v)S53@5Apq_{1Z}4zh^=aQ5FTJbAh-*a($KpyV6Ilu3Oa)o^isJQ`%uz; zm)X*cuS%2{eXiP9pBQFdLFDB>!Y-x~OdEB@qxHRop0Ow|J*ml+oYL!HhfqFy$njTx zdh_|hDYB$~cYkle6gvpLkDbP9#WiFh^NplDsPh`EJ+5q3U(k#6E{3e#l!z;PecV?~ z^0XFCq9u#SE48F8fxZJbRN{ShPW(#02&m(Q*>!8Jrt6}OA%+gwGK3cWu`cOi$JmNF)0@sSAXO*xIU_$Y3ZtjI0yj&IzdHt7A*MoP=Y zf`RL~U6tuXn5s5Hl3W!|Y?W*SOu69(Da*izCaoL@zvm6K);?08NrP0)fkJ(Gh5qCT zj5Q7c^|I3hLqTf5!>Dcy83vzBsN|#~J@3&S%fWASuT&_S-!u~x&rJ_02O^B(u1gg9 z?|3>0`hV981G)a?g)!)PpJuiR-P@df{-vj_q-3b0r0QYqtw-TQ18*Tvt|Rb2!3uMj zPDo?Eu(wX$V=kF-sJ0TfI>ID1K4K}fyH|$5($*}60W%mW78p(zxnvs!a6QIujxF?$#v~ynM=Wm84uAoy2an|Xj1DE#W~XH46~qot{v8Ig zLdRr<5M2zJ=d^~^Z$X7psvpKo)I?k|;Qb*#qIIr3oQ($Tkmk?PtT2zzm}0c?zB2o} zaRt%F>2R?|Ai=ncj$DzLKw>fvScR_IG+PU}T%Hv#Y_vKY-r=Jf&3B9w$NC%W5u5}U zzZ#MNw|WQ?Ldi=)ZYS-UfF*dg%S?e%a@>kyDyL^DYFEkuu*ZGY1PiMK#IYcl!BDRL z;lZ4;hV;wVg+N1tk%hh;r2!QPRkR{j33ws~W0{*}+Ar95e%n4=ooz#Bcdda(SckSS zPfEO$+i0g4CT!5pjKSE*8=J~KY_b2I0=vFC#`iA?7DZrwvf8+1QBrc{&2-P9Uwyk5 zTu=Arftr>UXJWV1th|2gc~-{HFE(Y6)au+4!a#(31F(B)Zlt$5u(RF;(-8Ca z+eLD&W=5fAB{kwNUC4gQe{?2c9cP9}0aE)du3Iu(hH?pyT8SwnkEY6i$o5r8+G;9mw= zYg~0n4EFNmbwS9-F%F@k8_o};G!)Q(`FdD|C7goffT!f=ByG-pau~CeeU7aBcEL)j zl~KrHM;8`^TEb=1{)r;~H=~FdRL-||$Iphm{joSVmuNBi#r0f@`nkOJ zj|lrGj`&X;@t-*2|4JOumu}Ti{Z2h2FGkPt+!?{x(?2(l^&fuJ7&HIwZ9EbPN&OQ= z{3nX|PZW_GMTCiNINLx(MH{4#t!t4pnUT{PqU}ne?P6rKNrO!I_htPc!}3+OFAgep zHe==h{v$w(DW$J!uXpjIhUVbEzOUM@@A2DEZFqfSoo!LlJ?0v#yY~9sCt!jCu$sS& zaVumYci@=v@p|zRxO=|;ZKJR3N!K2N}nBTdBCYPP$V2Y`S z%CgIPc`^Ip-29V18gwwRkqB9SI$k8AG9hV_a-*)d!3hD#qJ~Q$w&sJc$_I@3bu!6o z)hxIIew`9!PLYYBg_n)z3p{Q5SXX?pT|fqTBPT_;GKOoM?RA^3LB@xkBU%J9PCf|; zfpuLLizZWn$uS@om4u(NZsC)UywT7fr?^{qntfBoEl5ldh>?2i-S%csN2BTQB?^_RcNI)-tI^US?#ghYb@5t~QaW5K-66 zSO&wd*YCa;r+(z<>O_I0+5Z*_>}Pc2^Kzg>W-8akNErv%iqdSzI^dimx_2lwMwd-o zh23xpztDf+s&?~qwQvh`*9w7roJ&^Z5i)4!<$G@Mg!k$s4GtCJr+cFB|^JlYc$t|Q_)gltnfNDc+Ef)qR8zBlmi?dJzd^ni@f2C!iR)467vywigzoPz znV{iQT65Jk>ydqq2^MT&@%W6ynV*|q-hIh2+i79ec=Y@+EhWWyi$`52NZji^)81Ax zCX|7gXdgF7!gi^S(hx`OyH1i;j`Oe;p29`9#b6h&h$w(_3e=t?ku!xBIKcKZppax4 zLW4}Ij5+0k_W7V#KM_(RlS%D`=k}RH`W%B1pEmNh{b*J7yp`L(@1%QqzIdA3J@49M zQb1zw2d9;{T~-lD?9pPXFUPR-t9^6BRLTsRcw@)|)Ftlpb7&h}Z}R4| z@nNh!%0{5=CU2Zwo*d$9W9-99K_*~H!;&ah+VylbP^WJY(o22QeXw*@w9i>9tWv({ zCqR5zj~NJ)^x)_zehlgKB{Wb9;b}9m2!JCMJI8}^BV=iV$QRF4`*MYlacuZ_i6rFT zqlUI{dSj8xt{{aIW*j0rRaq9XYo&GS-vruPj*!ZW@!>euK^a+u*bf?@EIGXN7@i+I zfU;!B&GF8RqAcZCq23aj={ovNLdRxhigCQrR1s6j6T4r8EYQj@3!KOV_N$|kO9fJ} zGJ!?Hlb==c8pb!C6nbR|q7UN$#{CTV5959$L-0(>XTS2EHigUjBB-44yPUDG6ThHqewfBcp%QfDF4A}*e-rK# zf_2W{T_4FDIolpBMi+R0Nfq@BwX0WHAgEJ@4eAwk%Z)CpZC|j`-{u(U3|A1H@o+OjQzm*2zt(B~7AEnxnQZzUTT!N}9s6$YM{96Ef~e6(-i z^^Me3i}tUHw(DOUS5CCWaWAjK<49Ufd_v4q=^?#2mtKxZ#1>m5!mj>&myKFI?)B;M zdTW4vJP`MCgXY-^>R)%;^sQWns5ag6C>GEWa0wJpO;$Cz_>BiZc`ON`x_aqtQ9z?> z#pUVv625Q!&(>H3ur;=ayl%3fO{c4vxw}yG#184GpXsEdTIsGcZL zs-)g>P~I3{%A-EWVZvORkm~yGLk-SEcV-}iwSsifxxU8*xUOqMPW-Zj#*1dJ|6DA+ zAQu(CZ%kL&L^F5WPcbnTnL9Q1eTpgcC~p(vP)z|T#lSGHkW<aWi97Wn;=Km+=wd;FhYcC04YOJ+6THR`8wjqtlad2qbS)|t0R?k-0i6mNcla zKe=^&=4^Lcmf}s@!dV&p9vSs?e%05Kf;KujZJVjoCDUzZFv}7C*wv8mOs7|GWN=e0 z(WAk;;HLp^=1c90Wklxtq5Imi*t5rTmdgzeQLZKtUrn6tY%WEqx8951=7NqG3wWj5 znfFjTL2t`emsoMzmm5NM>edoEsp{A6(4=^?#+8)$s^HWMS`j&{Y^s$BbHb4;XDjyH z$EcNC4~$&*yt!*J9?#yCR3C`}o#{lLdJeAF{h>@O)!nx}S|lHTzt^vdSZiCK(43Hc z^67#jj*f!4H(@mD(c?FEOhqTkd}T(sB)z-?{K={qyR(72=~bsiwqN_LMB0dId9p9C z1#+@)gr%Qz$JMB`b%oA(F(G9v)b=B1^D;l{>eom_EP$lJ9iTv(gJPw`XOo z)Y2SG%_+~%SPqu@UK4(b^e+%5U!rc?QJVIB)j-Xr)U-UH0{QM7ax#|>sKC8mKyC~U zLClwswd9+_4+cczb>t9qWOue!C+sXqOkxI;EJ|y0Ca|kAPOOfX%e7R(^|F90;F>p- z`=$s=9JU=386wGy_RbN{jC8%c5S%S6&S9RRx5)sj&!w$%an#dI81=7|z7XSrF1{A1 zve=L0=j!iXHa!7;JrAB{J1^IHIJ^`pam3MLd2h~1JK;v62v0nn#Upa$a-StDVbz7D zv#Iw<g#Zu;-t~C1E%iL?gafuN3vDC~_(cb3pciakQLuJd$oiZfNQ$?IsY?=q~exLTJl`jr}NF^N5zzXXy7@kqY44!`f_t08fbq8l9{wbUG72+^?zg%J^ks84of+zYLBGPM86iL>t&62iR*-{a*=wEU87k zW~~algU!DQ_DmnN-<1`; zJ&+uu44^N9U16m5)K(ye3P1Qg*+g)D3CpPa(PgdEq%ynS9oo_P++CuYb-KKNDM{Q{ zN7-}x;a9wT{9a@L<>3PyAjJl?xF`<*EP7jSgBw7g(&UqP#qHl8*0ynX7W&mB*>ffY*9tcT=$_u<^?8JH96Z4+djIOvz%N{2QvkP`zPBpsDR7xb* zc}V3kl!W6r;Z$XuPD+R$IKTxaR&*K__;NQj*{ijFW)%x}UvJ;7vk448+g#!6g`V&m zB*1?4L75-eY2CDllK7)vtO%=-89`ZmS~rbq1?IpXCx0!)@r=4FC=E1x9=0~q*8plY4||dx*CLz?V6utOekd#gNKoonVM5y>qiYzxr&MeFOO)p|y5mJU5G@>VTYvlwfVZ6{)$I3i9LK^ zm}sRpS|@?3u7|!C(uv;xNl*t5V!YH~I_9Fs(nD;PUa6&Z*+wWuSz%Blka%_qfk;CF zk76lA<{qJVN)vyCzUMYa_$DDLRsC3!>#+<*{Iw%8Hvybthloi&CAO48V88XVA`pEl zOA|hUq4sv_S4hO|B%(J6T65%qV$>&>oKu$43fmLh$u-~ckK$jbd7zE{O@*1B4;cIK z;YGyQcl9q4XMcuTd|f;mBb4@a$?yM}2J719{Qp5C?0>8cmU^Y{RXY&JeAUj4V=@O# ze!E9+G$2Bq_<0Y-Ws`>I!*%c_C?}=@)*w-#L5(fv7PkSk1FK2^01Y^f%`&( z_@MBQUe4?5xg0YUw0{+X_k?*ngny}Ick2!i;DPb*Z-hze7?w%#+qp8uw`z6kNj(!v?yk`Ov7;zUTj3=ali zDl!jZh;?1T2jXN7v|aZKtB}$Jo(V(}!cYnFRiSiip)a!f_?FuV(`Fs!Y|TR+gNoO& z?JMf9l5|*aI4)G$rt(*XH#fKJ!l~AU-xN+$(MtRP-kx0I&5MEjWT&A)8n)%KpDu-a z2$k*Yt+~c&BR^--E*`;%kO%a_Ffaz{Nvj zW6$mzo5q`39|mrGdN{ZHm;KkD2dymU3>DjY?}p3!Qz{U^wL$1~h>TU{Og6x`uPdK1 zR$Sjj;|R_3rihtmMh+wk#o0UO_*mh(;gKuA<};9=O=rUcNlgL@)g2O#>n&=Lr3XkP z{2XtezXE+u)c_DkPcOxAgtzVKo3Ap~aCf@US+QSh-#x%EN{FFyK)&gJ7pI)e$}Ddl?>I$H^7qbOeHO`;|sN?RO&yO3=> zKu@iX!9>q|)bBGABrGt4-evpuB{Ew%jE}DT;D2|z%6QoaTy7N9Cjw8l?hFD>0%ouN zs)B(8nZIXwd%hZ+#0I{G!AZc-ST_*4qj8I1G(d+@Y=5*yH?(Ei#(-YOrzrCaEHk8k z*r@mN$WK6oI>(12WsEt25Mz9?PxUt`*nR+jAF5f{mCzQCUro9!%~t(JHb^sJaIOE<}j)iWm4a6GdECKlYb6PXs7!p)9XlS zwN{n|t2R_xt<^=<$%^f|#+d$I;e#*OaKJJxl>?+HdlC#Z!FbF^vV@8PlH=L%v1>=3 zHOnfj#0Dn^T51j^K;E*?t@dBFpB$6`eUt2Q)C zGWkTq*$1Ps^%23luT)jNdbIvw!$=?f(jw0z>F|>?TODu%M@@i0kgVBc8qU&9`z&#w zLmkJyeeHcPEr?1wjK)GN5Z;XOx`?7+1sqSKgtw zy(PwCL{%2E!ofK=SLjC1cfz*EIH5)_nH&I%YKHD2xmAEs1_U+_Dhd?8syROa6IJOt z?hQiSU}3Xr5h!l`h2Cfzs^zzPI{pE}nxFmy-=4{^HZclDh#5_N{5;4;Lh*4hb$x1^ zGrlw{yCDRXtz;zwiFXBfinqgo^SZDLXHr2ESDxAa+EwV_Qq#7lL+MhZZZ+P|?^f`{ z>#=xTnuOG2Rvv!MAk(3I=&7Aa)h>v9xfgpt!Zej_9pX)yUo7|YyETBtqdun?ZC&i9_s<>XVITgRtrM6tteks&HSdiGl$EZKMNGh zris`lWFO~;D@#iH#Mskn9V>Q1cJu$H?mlR`P;=?kWR7({q4Ji`T*a%@!P6{-UtDlU*~+>!H4 zJ!fQvL(`?)!OmaNG9qPUdH0>e%rB21C;H-ng5dG5kn@2-V1+fvz6y``Z;qyrQopnC zYFNknk}^xkJ>IFdD(*ZeLlCsi{87h=oKq`zUeMn1 z`tp~fBx8Hx#l2H^{BAnz{7TV&0_MykilT#^QvJ1r;-ouG;@Z~xT3olg8%G7KO9Dl- zvAqX!0j38q_nf%4JzhoX+?%pi+m)P+BldCjXRV?{4feK}NfzYZnvj=ub~GeGozHJg z{5~2gG@l#imgwq%E3djQ_2bjw8+%U(UHMpAK-xTGxt@BRaKdp&f!?;xyyt?+6xH;^ z)t?Ym4HepwF3F4?(3yHL^5M%s$=edY%z^dsjnAJiYJdH`czHe1?EAZb)r~?OpSit1 ztQcUZ&Xig{CoQ}rtb4N3$?io7FB533UYR03h6u_VAxGO^e)*u9di{BTe}9!$FbG=e zEB_hG3zb@Og&A~XiLfX|-nEcdvQ1}IxW%HFOZ;~<0@nn32s?-T!w-_PUda{^xl3b2 zj2=1@P$8VY<<`Um=^`*!w1ogQBwda0@htwQ4@RqGOLLAXt$Gcz)@@*ddLvi(nMqP{ zUBId9{<27v?9)+z`|%~?0)=Ydy_)qVC9Uk57KQl5u5c8yqn%+IuQ1F4CDd?S z@YpNkl60K)yK9qweMLW?*^$h#;eeG9oLhJa6(!OmImC^(}G*8fZ1~#8!|<7 zyc_bgh+h-`MRf4i4UTXPEvRdTC|JIde1ozPqAQYWDsho-vYQ3qCiD0ZuNg8~pm*kc z1M|tW=|%Y$N)q1g5q>hg(6NW#kz(!7Vi%f8b#tOyT)$oc%TAJJS z*t?XUnE*p$s?G_b_XBn>>3J5p5iYr9c`23lF4&L`|o89!-v8J-9*c zNb;=lZ9U;0t4nSGP;piW2p6X2*W8ViiHQ5S^R?;Z+lO&Li;o7h__8kt%nS5GuZuse zb5;$rx#ZS=84?HsWLt&BBM~XwqQSQ=Gl4r#cItTTuy|DdtLcOt03O*qn}dn<#}w!b z;05^#`1t4Pz~FNdkd#8!epD9EpGu+L60356=HZ|+XZXsS{kPVcgYa+0MD>cM7XD&Z zH&l@&GSLIw*^H&|Pq_dyW8z0QbVkO!)pC-OC= z`rOaUpT`dU{xNs&+dpZ<|LZj3|0Xi5xauD#?BAWRw!RAwM<@{jN=7S*_JeZ*r&Ue*4D7_y}~euYQZ_Ai7*u$lr+peo;Awx>d1nMYxG zr^cgV2~jSvM%Lt6IfqfW5|x;oIL>k?VL48la4*A2uzds)Sbc=VYxTFrGl&@an zg^Igar8$QYGi}IHmST`|Bx|C6Hw_t zS`#lnp$rZtX9(~=Ox|@7n0qD|ag`Y5$pgAhq4Pz?#U-koXgM!2uoNbT_LSiE!Dx}y zC6VZJFt}OgJ3cBIBZ#=p5T?=y%6~OTGX*+7i7ChkpUqeRTl%tVt=RiYZT4MN?2lH) z6ZR?f3ZL-El`KvQ@K3(8$YIc90Nd`^N*v_`2vA`>3QOH^m%8>g4)4bqXM}l0=YPKv z)>`>!{lcS1rpYb!>dG}YJYvs(Rv40g-1HE?r6vnRZOs)WaI^FWD}=yzBiDhk=iBF2 zU;l^zp!Ts^H_?w1zHMF*3eNQaY$V}5)YG$sBhqu)4Joc<`T@t6qdYG-%va=WuUgA} zLS!ry{uszJzjIMnkXH2yJTQwe7`q&x>v+dT!1xO$Y| zYdv4Ic3V^f1m`hvjCqv|6%D4*L81WBV`k6OW&xaC`s*gJs0)#(2kG2z{P zy`3)#enArQB`ir1e&JO32wF3|ZG5hjA%dcU0dtPSEToc@RYS%_RjiW?5U3Ikkeztc2Z@M8Jh5ec|Yiqit^MaKj5|2|IGujs~e6UD=wd`_ae-34a{@P-~!kS6gH z9Y|l7@u$X}>$TrI$n#q`HN?F=PQZV29CCE7^bRxC3u&mbYt0@pwEsn@bf$=AsJ?9e zAPosUc1iZ0gJB!9enDz{So5CRL$vyc!VIKPn=pW9Ywf2QsKu(_^wlmvPk=>i_g+rj zWw(~}O6N7XHeKo*(wfXBpbVAb#eIL(tIB?&GOkdcA#27i>0D`10S5_%IArcRLBCdi z*eYo&Jqpj4_FW=WPz+woIb5ZrCdr>_NMhP0Kl*E8teqKQ$4cGf06gTO=dzE_C~Ef` zMTV7KDq?h(yCrwQx9>lJF!reOpc|Ds?FQcx4!x#YziK|&*^axIILyCmszsYU9_|Au zb7`|Uh5ZGsiDpD`%}Zy#U%raI&P&t*?HlSCp}1B|`iJ!9p?+{8ByN| zYM(_|tyd~pKoUpgqs}2!p@Zj8|Noa^>LPtYS8wDUm5wcx8Q-0%9qRfyc;lEwY^szF6-|t z*mrOQj3RZI&JtlLy^#|B;7lT&f0+bZ8m}^S$z;a`l=scvXDQt_ z(r3{92w29Sxo4RycHWyfA%R_ZC|9zL0YB~QKHH!yl^^v_BkZ3>*#Ef^#@!bCA6f__ zmXO)&5*tTg(C5;N9|qp3e-NJ0Ud_n65lD3X_~V9IRHMS!XG0sM#lTk7?xX=O6dIRimtY}PgXLsyKC2D_H&az0EVDL%jxQO-y~|i z*w3Zt;S>YYoDu{XqBeJ*=a%)#VOl_#TwBQdzj2C&^Zw6} z**<^a6bsdun}2YMN`KbK5(3e)YhuA509YeC*#NAO)%>A&81=z_ z|Dkv&6p{mfr|qh#8rf$UG8{loH(>1GVm!`rKp2UZq$oB+e6~+k@>V(a55^;4ie>Q! z<3YqPaJQiWj7J-Q@sNwUD9GK0e$U;8zOMY&Ww0nCw+Qhxy$4@W`n9NByB z99f0weLi50%;6<4M^@Ysaoo3|ph0czsCJFoBLFJNog>R@n;F=lbxQ~lcRQ$-;uX|& z-ETs?;ooqIsej=VQ~x7Q@tCK98_tgWk&8K{wrn@Q)=6t}mw{`^lgMZTTVY+5}j zFsy6v>P&QX*WVP6itl$_`v98ai&r{an&L|J;tVl%EqjJ-V)3qbCT`xXdG+Lrxb-VuZ!tU}D)8TL{mW1Aq%N8Y%63@rh zDy;opocpG3@%+n&<2ifl6tv&QiYPtZ(0u0xOGu&0SW)s;{QpH${K7=P<{_B^G{vKT z=ApSX#T^M4A4_u<1DJ=dHvTgYEz%YO%tJq3zxF%J8PSgtZqrcka{h&`(yCDex>|Q; z8JR^l_cu^p60>~w8J8V9-FM5s8-BJM4wuKyBmRo}b#}GJRm41V(?#IWbr@SiY8T>$AVjJ76n60K=C^0Y=*b^Od|0|QrmwXe zCz>W~lpq(uFNdk3A^McT7mz#x7F^WSRP0H-8V-6}j1OvaOyUh+U`qrha z=m?x?lYeUR-)dwV|Di_a8Hr`SrrxqYfC8RwZjB6hv4H|$8=OMfbzm+_@yKjs zweIJ?Sc)Kn%v#9~@%`a_PSx!5+zjJ{rIAkSp9CXWP`qUuqHdI6gu4lQ%}$8KB6ckO z2c&(Kx}2X3o7lGrAnlj9C=a&yzY907Oo5-DE&;;rKwmJ}ayo%)ptZ^WSD2!KlJq6j z1W%ff0XM@~4Oc*{iMPUm6eBw&2CeU~J<@zoO(5dg_$p|tD>uoYT;SiB_L_e&?Ir&s z(_XbPp9Q**`4eA;cD^j9Q$6$9J%%qsj>37&WqA;OUEcJ*yQwnp_Saj==IYGN^U>x( zwYK-|H)4J}<8{9L3#K^S+3WA<;Gp@HYo9Rz`wW;P`(vLeK6N~) z-#To>mQzhWa0>SUghG+M6hzZ6LE}Ghv{>l0{Jhby+)cS5CJ*WztMyhLYb9#11OnAK zQ_h_uGxc#v?G@xx$RX{T>-6M_mur+R#VvtR{|9?-9uIXN|9giSGh-cQ>`PeBnS@*#mpL%cXKN2Gm|v0elIAu>F$&YLBux z1)%m#0BWx|>F&WT&~6BI7^&Qf(U>m}QR`1=EXiaM+gQ!#xuiQVjdFqURy{EpH6lNK z73|YlBtqlNF@(M_GlS4>ntTMhPPadSC->GvNk zm*SeqGQdWbMz@hEZtbtK=X=@gfR-AB;xqG`8ZuJk4E`@%9}}idO^)mZ7{#M->6+7S zz(%$rsSQZ>L7!qzKx*j0R_dteLmE&+yBG1LoZoAq(RlJYm+Vb@9)=pa>ugTvp71mf z%IIitZq`9mNWJrwM!fDvytR3^3#u@Ffl!JM)eG0X8oH*3R}IMXXqnhG_dxjZuk}C; zUDOQz2n*yoI{=hoc}HHTS$a4ZA??HUP8}(Q2`D`yv0-$}w*i(1NY5TuZByTRbwoWx zh5_=}87;{Kc}!b7hsw93z}dEUs|YyTZrLB{*cj~5lJtP=z&P9BPB#zr@uwWA!#nAH zBo|KFjydh*kEuDWE7w}MiDvr?Fzs(lLuTmZ#!jCWh-rU>55vX&NKA^5?dKKG=jvbq z_ifadbsklf13%MG0bRT9r{#PQ&nG{%@51|amp3i3Wxo^0pO%1__Pv*sShk^V_we7V zPt5^L`xi{6J+esMek^roslt?*9cG@Y&}x3sWuKM{wx>o0DUZ7yp=B_Ey1RyHe=X|l zF8N8cgG@Tav=4o*st7)}jbRpG+B;`|_S=wy2n?i$XEvTB%p8+xFXkjNA}PF+`pB43wS` zR@(15s=A17Vy0(%)}&__vA@%^At*ib+QW&}{Yr=0PaZsu)KPnj6sVbB3ja}b$JpIn z&Qii;OR=-|_@0sasPh-@8=EfxsQuzvsQohPn`K@a_Hd>16kvJ*sQuf`QY)JpVc!)O zrypLp61MW@SLp)uc=Pz;@@L28XAkx^0gy)?9pnM5&;^hc+F&^C+9U(y!L<K^|px z0neeU?U@yb&ZL4-aGw^&)#hHrRhfy8fvz_3c5th|Xy8emERRj+j>)OpYG3Wkbf7k z!3dntP=oO$qro^dO)+q3YS`MnLT@l`f}%6vgdYFhVBB{;ZtqzFy}_74Z!p3~Swo)o zMRUwR4Mw7sRSX-)Tr?E@K+##Apt%se!6;8}FbaSMV`+yR9A=Y8F!T2(7-=uG>2%QY@2I?H85XKL2)C3_-4jK`F~n5jw9O6vIxB&Z_Cr*>w<|WeGorOk@vrd*UIC2QZO+k0bFgFdkxF+#7cbC@K;R zrm-9c`8j4d6k7$}(1p-7VlFF0tt^tn@zif#?AHizEg*!}m_G!AyDrP@I@P`PRMgQ^ zRbNM)G;aL*_UX>&jk|^xw(T4PcU#AryDc*!?Y?9M`-$;j1%aEpWd|_IGRc-oRvNT8A1jEZSdfe6XCm$0F?`Z`2!OQDhmiIXClhBK zyaCew<*yc}l8*|fZbeqTdiB*wBYEeAQx6`TxKT8-IP;6Y9>uo@>?al3%q;4-rKxuF z+N(>lqlAQg>!c(*5Lp{e-~_g?K}#~hhY3_E2GbrqKF~8{GVQaOO#3}>)L<5G<)xEI zx13(+)Y(3Rx9)OP^P+yHSl?7Vzvb3F{}Qm7p90A?=rS zH|_vP`}1^|hwf&ES4_2yY=fj)^|-35KqxzH9Z@DidWepnWSSOJX}MSfm)1e zV>mu0Z{Rp zziy0F*h45j^GW?@jZus*`7ytlN&`Y{1|aQw*DT2_Opu~-f(GjYGUN3>PR>WK?6xx( z>tPSEo|5QC@CFEI<4kENY*J+c9l$CXSn#c=!fJVs8`5X{k5l%9+<-a}Q{E`ierBF>2 z#Z@>t-qUuNN{MP|R1d2z8J3|~uwD3=ZF}*9Un!eW94@og`Y~;;k&SdKw8?KPv?0`D z?1f`1V;KS3(zcFUT#DA;nE_grA3r@ni<<&1#w?ZTrnfY0L)r;(-}0`KWL90W1ii)B zfD#L3J>(b)wHRqOHYf8)W%gT6Gp1x7j^IlGQ?foVCF9Td1*T+Yz?5uD-_0sfv9|V1uPt_gXzcTODLqz-j@UJ2t<99IKV(#7A z)hVy*|G33W&Z3r{s%RGH8$CbgoVQ^xUizaj8P~a^AdukL3oXfD2>D>=tDwgSmSkD} zSG_N}we9G$WRw{1tur2Clo*H7DFgP|7{F8Px}+_6*u#`q$0?03ztc0-wpuQk^87>&dV2QQ!xsEZ zd!80D(lZ@YX&y+=C|l0_M5x}^C-E4%Dbot64$Sl{GHSiXZeQ#bv!M9=(04g#lO4I> zk<%*6eEw=xYZ$vNVaF~{4x;2YpKMdR9xguC^-zSxCDUDe`IS-V3 zID^lfaai$WnQ^h|i?%&LE!IOTlQArQ8CB0 zUDAt$PjXyHHUx@>gGakh_`NG$krT=f)jHN3Rxb+FV&p?WEf$RA(A3%=e$@K8<~tO2 zao2}KzUe)6S`S<1QO@1EZ>l07(H_;fX0Ou4*0FI(xCyBUyWWn&zE9hWLOt37@e~7d z-k4DWKUo4J^w*-hD;<--2(9&b>I_F*hQ}tEm8J37iAkx)>#rz1U&~XRb;{lJm|RBP z9Y=~P?FT%?ugj=~dOrCkm8g~dpPV*F{mN35RiGAA ztAaL3=xRtU77jeno*q)MKrL2>(23=4i#BC1_?ub`okhnlg7^ObsQ5ry#9gO)i#|c- z1z{-6EOtmt1lCGCs*RT31Cu=VnI|O-`!C>adEz zL_#)Ll6_%T7>R7Zd5Tw=JjI%|JVn946M(09OL}tHw+sn*ilHq5q#^Kkj;R(qnz|RNFc$pbDdqs4B9KBuJVg%%PcgA2 z_Omq~n-hM8&QpBntR4w1<0^mi6t5o=yr9NG=PBCId5VI7r)aT{O}UlMQNGw+^q!tUy`Gd6Y5GLIggw$fWbhQ|5d+>%K&GihNBHE%C%|T78 zL1AU-YO%rga=0khcA5yLBM6>j4gbs6XvV`kw-$IsLcoG%KOlXhPL zPU!DmkP|urIH8Xv*NQWo&{QMH2^}Hv4st?kghNi~3B?Ot%5QX}z@w$tZnXjnrf)AM zLp%@aqz|6g1UW_fe4^py!n#q*+gf6vwOW1NwJC*8RBZDw0k4CAsJH-#iq*Qs>m&ER zt=Uq5*YD%1?RR381n&vWz3D_n&oqs2VtEEBGW4^hYkpiT2;Q>IFTw{YkP^BVQbLyr z9ibBySrAttCG>Biq9;&7uUtCOe>98#6H@C-3;Udges2Sf{c3d#dss1pX|J+P%v_7k zw0|Acm)~3ThiUIw%4FJ`;9#|v+JIY30z%sNvhs0VjuYc+)V{>l%D&fIlb?GTaJEC;_=#?7PFp?v@f|RTaskh*igWGa~<3GM{KCwI<9r>U?d{WuG$d*W9#84Zk8}R zn=(w3Y+-Ah9zkAf!E!*9iSw6xxwB#KNZ=T%KD2q`i0~ zbr3?@*PVXT(4815V`LhpotSxGTVY{GQ)~o@YK_Sf3x<|t`&VfmSEWgA?N4i24ZUcW z>V_ejhZ#WFtiGPH|MqisbE?K#pS2^)K3fl(6 zso)ck7e|c$p%zQl5xppW9|rCM2c)(BeqVxhInxO302!f!fe|{NUr_YOtWwXWusdwh zCsJG2Cy!J_xi7SxT5R7MSbF*E{-u?{CpRqSK!NeYYJu@CS!0!_Sg1g*V@D4Go}&Ik zgbUxsDTt@2m4pQEs_97k7YotV-SWy59&KjJoMWtMjo+$Q3b!slZb8a z5!u%61zE*<|5jjB1qH@KmBOIF*lGx@VspO>j0UR(#@Qo5Q`#Gmj!U~yCVJsOD^|nM zioJxiVjk=GR1@9VT>vRbt!a(9m<5aH(Te;SMy9<@ni=MEIqV06782R#ml8jK!z zLhJHj(NS?joK9oSw`}H;Y;^6Ctb|SKUOZ?px`g`lLQAqn$w>5{3vIj&;UZuXfeYAr zn$5=wT9O@~V!_jQWb0;Vi8C?N0dd7C^Q_l74O2Ar`Vq%R#A3_&A)3b+pn3R%su(IF z)ZiiU8`COAil79BxTa<$s|+~wLo|A_rgyMLTnkA#i?G^F0NFvKsJ&NeZ$%8?RFB-9}<5TYQVZ#pnj+hQCYU zo!?w*ULzIqdrRiV+>*7f-ICpDhH<--pSOs_1w5XJj*-a88S;vpSjV=q1bM|0qw41B zW1Vh{eD~x3<$7aw_{?Nn(!x)tXGQfW1oY0S70z*IK;0;a54ZXE zCvpNauc+^K%{N%u#DN#gEpcgp=8?T+iB9t%AFW7X&oxk}E0qmv=kYf-#j|jX$2Q)q zqJV1=jxFK)rft~OQ*V}!HXRhlx*rn6F7qsAQg9ZjS(se=IE9WgTVDGN?sh4^)ZfcN zp$$9k!aRS z*E@3;Qdtz7Kf(}@-nlUAd5)6xW9%VDg)x5={Kp0r#%so|&WciPpu(6BDvZJxB~-2! z`yRD|q++!!@X*GSdV{fFFGi91RXw7S0IhM9P$%0tWo_V`p}Tw61X)R&!gf6^fM9L7 z-x|SV0R9WIGW5>6o{aAIc}hL-WkO0pXdeY zo$FB2vV~`l5Xg#i{<37}wR-1SNbfuc^v<xGdC8rXA=m2~`&tsrBBiCroQV zj&EqZ`K@xyqVCDL7}@MLytc>LP=-`2c#TwSW0RNt2(I8K#jx!sy})P;A?XxUCeT3GqJiZ^8ZZDH436!O?JW}%E(62<`=^nuHc#x_>?ar!|{B*msVjjcp{N$Zv$uBS?TUs+CBdm~*mfBl$7`iFW z1IZ|uk$ovjTM_di-G24e&*4JZ&at+>Cy|dY{hG~GxlnYiBkI?@LoO7j1Cj?nMDhUU zr@i7;w{|c{9%2J5sZ*34bdpB|DtYl3sx)x1yfSYiS*;j1k$c*x)fD79nK6g771LBBpy5;)-7ss9ucupOMWRNn&x5!#Lx1gQaaZ^@6 zpKJ`7oqPWn#qJ~lqgXTDDAvg^in$p{x>_X)M{+FEjbh*857NX$^je80e;dWF0i)O! zx>3x5VHBfyQ`9`gobi&7QEXyNG1QbM>5L|l47Fl1)Si*X7)CK!x=~CT7{#6vt&0Qi z_~wLx8M3QL2)~`ItH@g%O-Dp|aeLOUrfVXon)% z*{ereaw68>hF4HlI-?F~57m3XgkF({L(RftXx>x=R=5PY$(J;9sEKuhH=pAzl1Ie= z|3xDHKuo0jEnpP8w)Fryf&~8B6=Q-A;;qMEM`i@6Q?|}H@J&?i_E7D%t1}gv6`ak< zXbA}NXchLOj>1{k67T*YdH6p<-@CO=xG4V0Z0af!FgC z5B#(fu`%BEd^~e$1mpo03w}eBdyf}?&6vGQAr7qy#nNp4vfmdYuP;>?8Kj0@-;6#n zTdWUMTDu@@IzL}!)YA5aMYZCM+9C#AF2eUMy;78qm`|}oZ~Dq*SpVWV0LXN?Ir<9L z@mhzmPJ7RVzs7wSrzQ{)z#zabunRv{jrzCIuD4?-6%?ggd94`4kO$XJh6 z(${0_q}{PMxxjktsbc!>UagjHJ{m^?cm%?vcqCQ@i=c{gF%ZS0!pIH;(F}?QiJZ1H zsquxS9Gc&IT^itTGriWUe$BsZXV0Avbv_ai{v;0wO6M7WWCQFk!^V!7KLFtG``P|SGEe2fOo$GEd%GlD`T zf+37YqvRT;^RXU$EuVPJ1W-EDQXrIKFbe{yL*i~=C&t2GQ&x5^+?2X`t3w_2ywC>vTSN79~nWRsg5-)2!DlY=-I@&9|p|fz$aXgi<`Qg4Z~2x9h^^ zQT?K%Q#F@A=CocODtiCA=u^wp6?O64H4Cz!VQ*+b)>*%b@n{7YkElU5F*yP7>TE7R zkks#XxBPzg+7KoS{o$g4DDiXQKuSiF8k411xyn*ZuP+cJ7W*Cz{e@Xb#Zs22~h&!YHS=XOB=7Ri`(j)rkV$f zgXnQmztRghM{SM5j!-)sA(rBm>VVQ{ZXYYh$h$@W-@#sJdLAQQ7>aQWb#d8s38%F6 zxs>7w5+*4y8w0+CP}bXX&iG=oZ2hTY0lqCee6kK#R>mieN-QOEIQRaYrRYazDSr6F zQtV4Qc@uM~HJYc6P=b=0tdo+^8zW*2klteZ%@G}a0U}a$B(hrimBpN2UM3B_nJD6!|tU_l0iV*am{b1JqS z%&Fz0&&R|xOj8;2u}hJ%ih7x&+xPP*J{ z5Q1tk?58Lch7-CD%griioH6Y)E8zF*Dj>=&pG8i;inO9SM**WVb(n(rb@pxoik)pZ z87*a3rS=ec{@H36j`FLYzt6eycLt}vaN1S$m>*ud#HV0BD4^z(B zYAOUj%Zk22_6kJ3d*9NYD;)PZEx%a4@P{1PNl1Qnt1a30J%>0zs+z4(_^OzMC_b4vzmW^IR{ zV(`1^Ibwik-{>4V!1FIY<$&p*AHQr4KTf6ECK;S4Ar$+)>}-u%F)%)UJYnW^5Xbj% z?!gWDtoPMyqL3U*m=DR?Qc4r<#RKmZZ`T?Bx*J7UK0roE-nb#MFq2ie?B_jM@mcP| zgEvP`E;x#Bt~-U12UY!|0A~tJz8{8ZtavGMIyfW;6}T84-OV1^AY&Ae(?F9^klH~g zeE@vU`6kNy_Hg^yDIyJ-KIcV0;(0C($mcxZ4t&mDeh>))w+He$e{fIZs;MHd1D|sk zZveYk21^W=?%#aQQi_1a;Wq%HvNJa6CoQz80eeahk}a>qH4VqJH*eBLrqO2naONFI zqj!%Yx+Bil?cUqPuB}%PRS1ioNr)FRTTsLj%K1|KT{?~V|Q=hQW@G& z9_@x_W#uUxXb3nrSaj0fB!+F)RGg~XGk;7gBgmq!rP-QK8P-0S@)Xt#s`gBuGykom zo4B4ZW4&0ChZjxmI_Y#iM|rP82J*uPZQ*cL8w(zvCy-1`HysJfi3Nev7IhhnX1G@Q zK*v_o>I)>hCe}*=sfZWp((D&V%eR~DHD~S>drv_;68Bx|IB(wl=Iq|%szzQ|*2K%l z?K8bYZ93D{6omrLzGQbzvs&R3hZ`Djh&zKD29`VEXixELufhE_Ko0$@T(b@KDoO7~ zE6m(gN7yC6^Lnp;f0!l9Hp_x0e$+P0O(fRa?v{$gBMg3r8-w2=u=@dPuezr1qX#`99q`&N@9?OoO$bOP23^5GCI%23_G)=n1-AS^CT6^s9~{oU>ZVK6 zK;dPRQ?Yr!f2hQT7+UbcwA|a45WMnO)VKEqN%IE;Y0Vo4_42s!>khs~v zwVk~68qrO;&Rog8+Y2Y@HaUzTj6WBHohP=8`j5d1ywUtjpEEzx=PXF~Im7S6moAQI z<$DThvx@{nKIb8IAy|@B{~0dxkUTBe0kw6&=d5W6`J72NdiON>m;@M^^_;ovoR;aFw%rtey?1}BnrAoVZQ3H@Y%zAC_juuZ?EnGgZWe*rDB2Af z#d6D+JI5BzTv+&+dGYa9|LLu|KVP4^XH>9p(ZLx!;wXG2$;ra8Kis%gcE_b#rkS8q zYIN)WrhvGp;`E<21bZnz%l5UhKsRWEqE!Jc`=?DqtTE6zbJBIroHPDBT-#jGypC@Z zY^y`TL%ybr@*14@?0e-lc0*fbpZM9K6iDYRJ3u_7hy0^+c0bGA^z(gIs$r^&w1m{7 zrQU{RIfR&AFY%CWH%{GE@-3XlKJa2ttm31tp*tJ3yd1nRKdW=xG4B`jxAPPB&Dsr4 ze-spYfdDEo9S_aQn%!Hq_j7vTNP{(@G6eTW5$j$LByn1n&>$=|D^jvT$bF+qfui2j z?u~=PUWA$~^Q_6!`=i}zW-TOlo^h{Uy z7E`}GkCJ_Hr_pN3D!q`5d>Ef#TJH4j9~8ys)}>n>KeYpK%u_&79F~_SlbIy@m*!+- z7Y&f?FDdyHhgkugGbYCn*Z5<->+(#aljGz<#fnS3}?B5mG|Kb%`!;^ty``>?8ih3}UX}l@wSl9NW$5QrQ&iT4vylWmPojX>Q&Msmf zwMW|?2Q4@6`uL+2YmQeIm|jpKpZ06$?ouzcyJ#P}pMS7z6+h#qC39?#B8bhA4{8=PktG z!AL_oz#|5uI{*V0Aq4OsWo9gD!)@|Pc<_UmLZW?jA@~x=n7Ui1IXL8G7rs9;!x()3YJ=tkYYQ55Cq6h%#^KNQ716KBtBTChhn2=F@qiX!)?20Wsm8$ZB6 z*;}^v@>kZsm%~FSd%u@i-6H^H{{o=w^8^Jk0A*i>Z^Sp-T`RT^Yll$woA6|E__SjJ zy7@s88T~hu{W65Imjoz#!KLG^P3gtu7kOv6J+WPJG7qzv6vdNkD2h$mfTFmMkB2UF zE?6scj*mQhM%*uaeT2f%sF*X->~}uC+t9^*HiR!4=KYbOyMPV`I z6N2DC-A*KIJX)?&ti899+oDSgiO;{Q`;1&j9kzTJgW|F8rMM|MUyYElS8R8?V}C8< zZcQ?SWFN_LtcgLgZCT7M6uApU-KpIh4x#mS5$w<=d8k}tT=h90tW^QS zH>S@y{2feX8qhoH9~qM37JqVXIxPXwJNhk{K4*`N5Rp}%^SSCQ@G}KI=P0_*Ie_1C z1MoTHOZk8Bx*rRNNgRgg9Xu0QQ=ngoN#{lD;{=Z@nVsH} zWfxC+-*1(d3q(2gIypxU!&nr8fBT%(UI*o+d{}1v;a4WTS!)q%Bz8nk^8UySWuf(+ z*@~UI&YI)FjGbVmO{Zj*e+`PY^!dG0_V}A$M}w( zauJFOT-q>^4nX$M80NQ3EU&B`$iy-*jb|>uWS!o^MYCF;4?M=d;E>0dkGA+K>fDX@ z{GKwI#Sd|DPxsY~mLIf&9iT5y45TUJQ$UWJd^9hdH1$-pZk>y383a?EZI|vyY{WaGD!Z?TfHyggEbv@dm}o=KDi^X8&K@G#0=9?Ad{H!KV;4h>LMe8 z#`n5&?hQjGvE!>UXVnBmcF(R5HX~dRk5TVb11`X#|6vF*z~UaCBt=i8x$U;HqvM_- zyaC?54E2~>*Bl1=SF0TRZG8BvqXAwq=dTgB_Cp+d@U2Mun@jA)a_I-7I?b}@5XZik z?h@<&r_Q;eOTgsw=EMp|uDrPLIqpKy z&Z7Feo+fvH{aX0nQ9z`um|YlUyZYNFhLhcUC0ot?kaL-Pc2~LQJIE(SlboT&AQJQm z4D7qsf@#I>X2oG^%XfF!PEd<%rYG-F24Y_<4D^P{!`JtddCTgz#^zMuLv5KpvC9mf zST)@z#+pFf(xk1a#9>itp5oIYZe|5j!8Oh#n3`)Uy|W@HKCS{CC|^fhqlk3bj4G%B z-&hAx_Br?obGM%7nNu zVwtw*0aJG;fC#z%t(Xm8U?7e(1ZyV~uy#T+EY)eeZdtrZRw`VFnuJ1k^oEgXgC@N) z?}g3JK~86UR6)MF8KTgU68lVqy`ad4x^Itgl3*E|!@kmEWx?Jo2ZmB2qURgsBXD}^ z5JHg>`+baZPLbC#UpgtwvloIW(gY*Y=rH@5awqA08{5u9|74pH=o<}OpU zycw6>uZ;<&u7_auKb(2ELLrnrOI9rWD@S9nt}~DA71|-H6eJWo-p%53hz01xzz}TQ z2JzZ5w7V+m!8(LtBMU8p3Kz$9Bk3<2f_|~f$46V@n_g-cG^Ew}c$5rOQmy_$QH)f5 z_3S^bg7{$N*TROVdn3!)IpGdRFfw}e9~RXjkobg|iO8fA<8<|4tZ31(MTHbdDG zkB>!@Sg|h=al8HTgNAjSismvO{0|pij5@6&72T+>Nd2s;_)B6|0gq3VR8r8)<8aEw zlvX`#Y$P4RqT8bzl@zVB?MaP*gh)Xz z1F6_SxeIlM?t2t+Q_Z+uc+_lZh^NXxQZZ3IA2vry7m5dxis>m;XlzZ(Qu>U>n$S>> zblO4b%uX-H>v%cEnUJw-v$4+2;p>{VhHh?V%`+*dEZ~a4Y#amRpP`M2_jW5tIrpk{ zd?vBTZ{$#${K51HifqWE2fe7Rex?X-si)+$!~JPI;t?Q?g~RKm6=q$Wv?w)k@g zKRMJE>9EHE<)A(->u%hWkrwEqHE4~t6_pitz4u)7G4{6VR`Q`RjJdU*&{r~Y%LEu= z7MGC?AL>ir+hw>QVh->WG04f{bI9PQ^{4H{MG`$1aqlS>gWh`D!FL{oYLa63UhXCD z{^;@aY-o`Rn~iE_tY4MjAcg#G$iMov_z3tmb+$M1cH)B)Zs)e*b${TGWc(1yRrF0e z4&m&noJ^+}r{|uRT0j3d#aj1jfj`A?dDl#UvsVt6?{^*%k42Z=*|))=6=eu<6bBl} z+(GA9=^VwX;B)QImG9WSNx)2`hLMtmC{~3;AvII6tw#LV!nT8r1r1HFUDr z2VtAkXi+ zC*xjT50qlPVcn?Z<{eG2*qCX{gfzL=**>ssTermIhVr0X_3faasX_j??HVH&^-s(D z=wybYMRb(IR-!L%vg-C}v6aik)CSs`Zfm?&1N-`Q*xx>haLBsT8PoNXzwt6BxI9;t zV%l3Oo_i)?<8&q=rI^}|B%b4q>gtbV_?6bh6-1Lf#@`R+*ch5Wv-Xn)M0xRzoY1dC z56{Ka8ilNj2Cq9t@t%E`wt-G(w#$m1i*j)p;CC z%?QU{DuGtNWq;W5`6DajU+7iBg2RyNCUn~pL zdv`!r*(#vS)7of1b6ItM9dk}Q2jbStnjF| zHp>Q~SdK0uxX!pzow+K}b2`#lLdkbPX)MaC119qXpz06!4d7zGkFqv?2U4|$TV0LS z7E+YRzgl$K;5pC5Uy}~T)CPo@!jI0br){B@mNu}!k3&wZFOXBrp0kA05qbG6N2Yyf zXTrBcdsB0ZSgE*6qlr5nN$kZs``6eoonoP9{mHC{h1diN&!hKTWPM<+LiEzgwl03C zER(Jb8jCjUq+c}o53DkKCRE-&@+OXVZ2a-#NI1k%nF1UY?PneLD0dp3XB@4mkfi~F zN4PvoKJ@WSr&!+Nup8Ye*1>R!36_zTH$hIZl|6jk-k6k!fP~K;4my+!nEm5dznx-x z=uR;ZZw>jpFfoy;alDW3MjeKnV$?RcR51f)ufl}c@1?`+cXWGo@3NIFt$>_jv}_1w z|K%XQLAVj5a?i6x%%)zlLNNPkI?R4PF~m~*L+qpBQvGzX5~LX96w{3%!62Bu-U*Rg zrm=LVmda*GIvR z!m%TXlkxo}aPB_tiYThEEiL7K%Ys_0TQRGoGk!(rmjQ{?y?1HBZ1POrr?v-gzHPO< zZ*=l<5%&GAsCy4;Ujdr^hWf`xCvT!D1G=wb?R)$^Id$4lgH2oa#mpm3==Ci)vd3BK+_kz(cpkM4Eo1Y*}J&9|aR1J?J{@!e^+<@2DUk zw|4>X4co4ljO@m6boP-?4J;Yw{2Pb;hjEyW_GSEyn0#}13c_%|1{>cTQodnVsDioa ze!e@+Vxxi^@A##8%}x<^&7?fEoe~6lI+7>b0F;MOagy$di%8ytd^TQPBTb|y-;Yo+ zVNbpTWQ3xlW!TKM#LVjYPmyVkXu&|z!B}oSmiQ(IG+1&0C?PA_XPbF zrS(yK@!XJ9>>lDDV&`PJjrZC{tn=6?;hm3_+m*_^rh6Q3snPrl#%KWhlqR*u&@^zRPr-yPVR z9oWvrq8-UWPVbApgMP#KR+E7os*{^;>YTz%PCFn1hWe zV%!LMJx!lj&x@wldXhDZRlP>FZ`pq7(H(;Q++9cE#%D7Bny;h~pt*@_>mw!v0JC0&1o1%bEO!O;K@bg!V ze(}SQPb^G`DRuU`b^gVWI@<_$0;w~5vTgrn;-^l&h+eu+EQBd_ zrv3aGDFS?A5)7XhdFoRmn2~Kf#=5yiCx-Vp$=+4@mY|GIDsPy3l>e;i? z3FaK8gYp#O%f;LC{yjWTb@=mIouXN-I!Vyyt}**0Gl}xAaMTSynwC8m_FcN**XH9N z^SxR!yvO;rBy6{|jMU9CFF|yFJ6%1t{%u;TsQZ|Z(n@fkI^O}|ljDI=%a zfcnlj+6Xbs?bsMC`%Q#Ss!${+<7n%;%7iG20*GP~fGBdRKgvGUe?uO$7x%}4_97gD zC~D`NHifohKEM3mf$i9NL&kP&^6HxH7$M{Lc5E5ijv1F2aule%ljL%++qR!K<A))R$$$+lcV?hVq9B%2*e-w@JUo*La}c|3@C^4ffqaQa2MWe?9-b?bN;2XW>Sv zD>8)5J3pPf+p@lq4C&;#Ij|5yk+kXY$wyMhZ$Jn|Ui2c2H!>cvL`Nw4Vb;G7g6fN+ zA#N`_J~s+Fqm`NU#brNYEEhjiUnIWY(>XY=Bmb{|Y~cTo{;`!dX`MjPP;o&WA>j`A zMkYkj?~RqJ=0kx&R~7ej9O_#*bY}&v!`qhuyo2}iA(EqW*ijJ!d^#t=lmSsZ!h|SZ zXFwE-Zxioj!J9o14$^iTO_+O*0)+0 z%B4&yjClmJQ$or}5Jho`vB2wF|AzvyhtvGy{}e&1yP9H@g;z+s_Bfk93BEBL zRWz~po>AV)nA3F7(x;!PSwb6^ru#ne{sVp=QSg$LJBS=QRB_9+Pc^=K&))*HM~ndN zm_la7Lmc_(KG%b;M{$I4Q}QHV3_CMGJAbqwoJ1C51ZdePU~~il+GH!DFMrkpV%a~I z6r3GkL#&_ZLIlV1I`5AVtBw)5eX=$)qt1s8QS?2>3rvgvqG$vliq|y85Fyn4y_KCf zu|6)_FA6NYK>%g1Ku6hsd0wlp>1RtY(mN!he65iNjG%TUJ@KKMr>^1(CKM~`M0(B& zKqz};1MN%bz$(gKNX%L+YOBc?BaZ0+q}I#bY(LlQ_flfG5AS6=G-N*dbjLtL*5NC< zQ+^Yw7rZc~AFDQo0ffoMX$WB=HS1U}AQ`nkY|9%my7w=3&iHf6x^ly{&cEp$x=eZp zW(~cg%Bzn+!yCF`OdiNZ@}kT|R3LhX77N?YHS`V%_KwZ~cD5c&adTpEbNnzqd6;D| zGE_#iTlaxU2%vZDINk!$J1BS5*7H89&JpJYK=bXAygDh5lW$-M3j1_EZFI&swM)NJH$Jh zG9h}0P?i`L%FoWOqU^6HP+sq@z1EPd=CidDLfIF?q+x4Mwq9_uZMxtsOqhXC_Rr}j z+rED`81?H)Iu}!o^pJez!oh9Rv^21jD%IT-7qrCtqQ64}reS9+k zB}JiIpg~Z`M?1f}iq>?+$JRZd5+d36jPN9+IlhkiJilT6`|oY}t2slos=b)SpyHP^@nxRg;E5?8~nh? z!(kfvv1I}$)f8#ic`$_yZQ#>)$M5xwl$7Q?@h2u0l8XU+BH$&CnNDRttB{|Rs?&7s z&})owX5$tAgR$SdV5)uJK?2!#ix%UY&MQ2F=U&Cw_X3Rlg=?B45XQcE2x%7klVbVc z=^5>42%tEvyYVdmD2g&L_9qz_`#ylNuYB@3y(HY=-WdT1peVhXoGpz}E-N}&=JzLu zd6ruvxF#tu2^f7p=J5H=h@fZL!y}>j!L#l>y2rdT_WfFF32tD|_)y(T;QhkKqFYYi zkBP2fcML-84*AD;+rxD{96d3(8s}IZ^NUgn~2YQq`wvd+zaxQ&jo<&umh*OL8=?t}=f$?Fd`{d>D+~ zoYw#ptxQ7m`}iVP!7>D7wVx%FgqAiAB_Kihu`ZFpfQLO;K}N3ou@z z=W*7ww?Y98{c@YT6eG8PU>62L%#ru^j>3iKpG3`oJoDwLp7FCrug8x>Ro$+C^knm{ z3#WcXZu|M})A64bQAmW_D!v2Vr(X-}%{ zfGpQ?p#U0?4ZL(Yq}!mQSG+MxHv2b0aq}O7BF#|i03aw1{~;)D#}97r-UWl(FhKVA zh>o*KED*^4@Mu@|y0}DWKz1dX#LdXfeC{Rc-)4cgO*3<|GF3)yHZTbWWUsR#2fq;c zVj+Sj~KxM8jwYV5?CB5G!!>9APXof3n?I#A)m4{8jM#9fiDU)7=M5Uqmt}2 zXfO(a24i*2Zp*{2s@oxgVlme$$X+FjNTY-7EB}D(X9a!^;q22<5}|)Z3K9Bl;v{ss z@q7lM%mG=Tm=`Je$6XjSAp5H_pZ_mtKz0HQ$Qt`k(9&^O9UHy!8rN~ft*X#~%$Ls` zNzctL^w*}Nz1I%NfQ^flZxI@hojAekp{FhTJs|T$to+eoP-Yxqfb4tMg6tnS0FeD( z$Jyuv#cOK`iaR`i#EJVI21FemaM8P!1csm|)Q1?lFPoE>#bX(RQJeme{z~F8xBg6HIFHoi)p;#Ri ze^(fddk+?*e3)iEK(n?uim{-O!8v970 z@=#)Qne$CT@ySv}kej`lC;vJgkDBeNdoSYI$IWDv zO8sL6QLJJ#Y7-51AnRw6+1yTlLjJNOZWbju#l^cQ7dq8y(<*=c0>n>D)>YZJ=a3Jl zB2s|KPmI#0;@MFUKaqL>@Dtz4&_`icA%0@@0c-aBboETYPptXS&BXyt$P)j?PaMEP z{KNq8(O^RMCdH5Vu5-^Q^1LhNObZE`kcFnv=2j&!Aqg2BY~XfdyqAJWGrh_M(wVtU!~cVF2Tv((hH z>HXV~*$)$;2^oc*$xoc_f8;*!G!&YUZTmeTQ@o=l%U9`<*_ON8R`DQmBLwhJl;%UK zBfxB{wAWIe_`%PamUlVY%^04O0Jk@kxMnEW1hssFT8W zvabTqrCZBiNwS3i9?nhP#HOT^7vJP^1{dA=FunYR8v=OTv!w$(4nP18;)M|9GaIfX z1$D`qZczL)A-i3%`AZ;uLKZ}ykexJB7G_MyoCJ$)o<$E+LFPtOFK153aBoa}V??e4 zFbwUAYv>slYVmE;e6KkG@Hpvw0~0|?Y;W&IC3>S-nG-S$b3!ISpOA6ihYw$TenR(_ zsvnoOA~YfMJ@<#7*eOgHf6D?UWK;uaLPmIVvoqmsUl|?Xk)lYzD>e{Z_!N=!3EBOS zQo-^K-tfKl)GGqs(gI^C)8^dMHj`mgYDBP%0DVG+z)WahxfJIuEMZt|t!U7^x^o&i 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 literal 170648 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

b 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 OPFXG Date: Tue, 8 Mar 2022 15:26:44 +0200 Subject: [PATCH 056/140] [Cloud Posture] Add blank page graphic (#126750) --- .../api/use_kubebeat_data_view.ts} | 5 +- .../public/components/page_template.test.tsx | 113 +++++++++++++++--- .../public/components/page_template.tsx | 88 ++++++++++---- .../public/components/translations.ts | 27 ++++- .../pages/benchmarks/benchmarks.test.tsx | 15 +++ .../public/pages/benchmarks/benchmarks.tsx | 15 ++- .../public/pages/findings/findings.test.tsx | 32 ++--- .../public/pages/findings/findings.tsx | 24 +--- .../public/pages/findings/test_subjects.ts | 1 - .../public/pages/findings/translations.ts | 4 - .../public/test/test_provider.tsx | 5 +- 11 files changed, 227 insertions(+), 102 deletions(-) rename x-pack/plugins/cloud_security_posture/public/{pages/findings/utils.tsx => common/api/use_kubebeat_data_view.ts} (93%) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.tsx b/x-pack/plugins/cloud_security_posture/public/common/api/use_kubebeat_data_view.ts similarity index 93% rename from x-pack/plugins/cloud_security_posture/public/pages/findings/utils.tsx rename to x-pack/plugins/cloud_security_posture/public/common/api/use_kubebeat_data_view.ts index 529b1ca5cdd1e..3e83187ef9ba1 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.tsx +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_kubebeat_data_view.ts @@ -4,10 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { useQuery } from 'react-query'; -import type { CspClientPluginStartDeps } from '../../types'; -import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; +import { CspClientPluginStartDeps } from '../../types'; /** * TODO: use perfected kibana data views diff --git a/x-pack/plugins/cloud_security_posture/public/components/page_template.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/page_template.test.tsx index a9be5ebcdebfe..1c367cd5c57f0 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/page_template.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/page_template.test.tsx @@ -4,15 +4,37 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { ComponentProps } from 'react'; +import React, { type ComponentProps } from 'react'; import { render, screen } from '@testing-library/react'; import Chance from 'chance'; +import { coreMock } from '../../../../../src/core/public/mocks'; +import { createStubDataView } from '../../../../../src/plugins/data_views/public/data_views/data_view.stub'; +import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../common/constants'; +import { useKubebeatDataView } from '../common/api/use_kubebeat_data_view'; import { createNavigationItemFixture } from '../test/fixtures/navigation_item'; +import { createReactQueryResponse } from '../test/fixtures/react_query'; import { TestProvider } from '../test/test_provider'; import { CspPageTemplate, getSideNavItems } from './page_template'; +import { + LOADING, + NO_DATA_CONFIG_BUTTON, + NO_DATA_CONFIG_DESCRIPTION, + NO_DATA_CONFIG_TITLE, +} from './translations'; const chance = new Chance(); +const BLANK_PAGE_GRAPHIC_TEXTS = [ + NO_DATA_CONFIG_TITLE, + NO_DATA_CONFIG_DESCRIPTION, + NO_DATA_CONFIG_BUTTON, +]; + +// Synchronized to the error message in the formatted message in `page_template.tsx` +const ERROR_LOADING_DATA_DEFAULT_MESSAGE = "We couldn't fetch your cloud security posture data"; + +jest.mock('../common/api/use_kubebeat_data_view'); + describe('getSideNavItems', () => { it('maps navigation items to side navigation items', () => { const navigationItem = createNavigationItemFixture(); @@ -36,40 +58,101 @@ describe('getSideNavItems', () => { }); describe('', () => { - const renderCspPageTemplate = (props: ComponentProps) => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + const renderCspPageTemplate = (props: ComponentProps = {}) => { + const mockCore = coreMock.createStart(); + render( - + ); }; - it('renders children when not loading', () => { + it('renders children when data view is found', () => { + (useKubebeatDataView as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: createStubDataView({ + spec: { + id: CSP_KUBEBEAT_INDEX_PATTERN, + }, + }), + }) + ); + const children = chance.sentence(); - renderCspPageTemplate({ isLoading: false, children }); + renderCspPageTemplate({ children }); expect(screen.getByText(children)).toBeInTheDocument(); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + BLANK_PAGE_GRAPHIC_TEXTS.forEach((blankPageGraphicText) => + expect(screen.queryByText(blankPageGraphicText)).not.toBeInTheDocument() + ); }); - it('does not render loading text when not loading', () => { + it('renders loading text when data view is loading', () => { + (useKubebeatDataView as jest.Mock).mockImplementation(() => + createReactQueryResponse({ status: 'loading' }) + ); + const children = chance.sentence(); - const loadingText = chance.sentence(); - renderCspPageTemplate({ isLoading: false, loadingText, children }); + renderCspPageTemplate({ children }); - expect(screen.queryByText(loadingText)).not.toBeInTheDocument(); + expect(screen.getByText(LOADING)).toBeInTheDocument(); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + BLANK_PAGE_GRAPHIC_TEXTS.forEach((blankPageGraphicText) => + expect(screen.queryByText(blankPageGraphicText)).not.toBeInTheDocument() + ); }); - it('renders loading text when loading is true', () => { - const loadingText = chance.sentence(); - renderCspPageTemplate({ loadingText, isLoading: true }); + it('renders an error view when data view fetching has an error', () => { + (useKubebeatDataView as jest.Mock).mockImplementation(() => + createReactQueryResponse({ status: 'error', error: new Error('') }) + ); + + const children = chance.sentence(); + renderCspPageTemplate({ children }); - expect(screen.getByText(loadingText)).toBeInTheDocument(); + expect(screen.getByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).toBeInTheDocument(); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); + expect(screen.queryByText(children)).not.toBeInTheDocument(); + BLANK_PAGE_GRAPHIC_TEXTS.forEach((blankPageGraphicText) => + expect(screen.queryByText(blankPageGraphicText)).not.toBeInTheDocument() + ); }); - it('does not render children when loading', () => { + it('renders the blank page graphic when data view is missing', () => { + (useKubebeatDataView as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: undefined, + }) + ); + const children = chance.sentence(); - renderCspPageTemplate({ isLoading: true, children }); + renderCspPageTemplate({ children }); + BLANK_PAGE_GRAPHIC_TEXTS.forEach((text) => expect(screen.getByText(text)).toBeInTheDocument()); + expect(screen.queryByText(ERROR_LOADING_DATA_DEFAULT_MESSAGE)).not.toBeInTheDocument(); + expect(screen.queryByText(LOADING)).not.toBeInTheDocument(); expect(screen.queryByText(children)).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx b/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx index 4b28671a446ce..f164b7b92fc72 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx @@ -4,20 +4,26 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { EuiSpacer } from '@elastic/eui'; import React from 'react'; import { NavLink } from 'react-router-dom'; -import { EuiErrorBoundary } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiErrorBoundary, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { KibanaPageTemplate, - KibanaPageTemplateProps, + type KibanaPageTemplateProps, } from '../../../../../src/plugins/kibana_react/public'; +import { useKubebeatDataView } from '../common/api/use_kubebeat_data_view'; import { allNavigationItems } from '../common/navigation/constants'; import type { CspNavigationItem } from '../common/navigation/types'; import { CLOUD_SECURITY_POSTURE } from '../common/translations'; import { CspLoadingState } from './csp_loading_state'; -import { LOADING } from './translations'; +import { + LOADING, + NO_DATA_CONFIG_BUTTON, + NO_DATA_CONFIG_DESCRIPTION, + NO_DATA_CONFIG_SOLUTION_NAME, + NO_DATA_CONFIG_TITLE, +} from './translations'; const activeItemStyle = { fontWeight: 700 }; @@ -36,37 +42,69 @@ export const getSideNavItems = ( ), })); -const defaultProps: KibanaPageTemplateProps = { +const DEFAULT_PROPS: KibanaPageTemplateProps = { solutionNav: { name: CLOUD_SECURITY_POSTURE, items: getSideNavItems(allNavigationItems), }, restrictWidth: false, - template: 'default', }; -interface CspPageTemplateProps extends KibanaPageTemplateProps { - isLoading?: boolean; - loadingText?: string; -} +const NO_DATA_CONFIG: KibanaPageTemplateProps['noDataConfig'] = { + pageTitle: NO_DATA_CONFIG_TITLE, + solution: NO_DATA_CONFIG_SOLUTION_NAME, + // TODO: Add real docs link once we have it + docsLink: 'https://www.elastic.co/guide/index.html', + logo: 'logoSecurity', + actions: { + elasticAgent: { + // TODO: Use `href` prop to link to our own integration once we have it + title: NO_DATA_CONFIG_BUTTON, + description: NO_DATA_CONFIG_DESCRIPTION, + }, + }, +}; + +export const CspPageTemplate: React.FC = ({ children, ...props }) => { + // TODO: Consider using more sophisticated logic to find out if our integration is installed + const kubeBeatQuery = useKubebeatDataView(); + + let noDataConfig: KibanaPageTemplateProps['noDataConfig']; + if (kubeBeatQuery.status === 'success' && !kubeBeatQuery.data) { + noDataConfig = NO_DATA_CONFIG; + } + + let template: KibanaPageTemplateProps['template'] = 'default'; + if (kubeBeatQuery.status === 'error' || kubeBeatQuery.status === 'loading') { + template = 'centeredContent'; + } -export const CspPageTemplate: React.FC = ({ - children, - isLoading, - loadingText = LOADING, - ...props -}) => { return ( - + - {isLoading ? ( - <> - - {loadingText} - - ) : ( - children + {kubeBeatQuery.status === 'loading' && {LOADING}} + {kubeBeatQuery.status === 'error' && ( + +

+ +

+ + } + /> )} + {kubeBeatQuery.status === 'success' && children} ); diff --git a/x-pack/plugins/cloud_security_posture/public/components/translations.ts b/x-pack/plugins/cloud_security_posture/public/components/translations.ts index ac03b9e9df721..b203d0365a28f 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/translations.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/translations.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { i18n } from '@kbn/i18n'; export const CRITICAL = i18n.translate('xpack.csp.critical', { @@ -40,3 +39,29 @@ export const CSP_EVALUATION_BADGE_PASSED = i18n.translate( defaultMessage: 'PASSED', } ); + +export const NO_DATA_CONFIG_TITLE = i18n.translate('xpack.csp.pageTemplate.noDataConfigTitle', { + defaultMessage: 'Understand your cloud security posture', +}); + +export const NO_DATA_CONFIG_SOLUTION_NAME = i18n.translate( + 'xpack.csp.pageTemplate.noDataConfig.solutionNameLabel', + { + defaultMessage: 'Cloud Security Posture', + } +); + +export const NO_DATA_CONFIG_DESCRIPTION = i18n.translate( + 'xpack.csp.pageTemplate.noDataConfigDescription', + { + defaultMessage: + 'Use our CIS Kubernetes Benchmark integration to measure your Kubernetes cluster setup against the CIS recommendations.', + } +); + +export const NO_DATA_CONFIG_BUTTON = i18n.translate( + 'xpack.csp.pageTemplate.noDataConfigButtonLabel', + { + defaultMessage: 'Add a CIS integration', + } +); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx index c23d6599a1c30..660892df9d013 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx @@ -7,6 +7,9 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import type { UseQueryResult } from 'react-query/types/react/types'; +import { createStubDataView } from '../../../../../../src/plugins/data_views/public/data_views/data_view.stub'; +import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; +import { useKubebeatDataView } from '../../common/api/use_kubebeat_data_view'; import { createCspBenchmarkIntegrationFixture } from '../../test/fixtures/csp_benchmark_integration'; import { createReactQueryResponse } from '../../test/fixtures/react_query'; import { TestProvider } from '../../test/test_provider'; @@ -15,10 +18,22 @@ import { ADD_A_CIS_INTEGRATION, BENCHMARK_INTEGRATIONS, LOADING_BENCHMARKS } fro import { useCspBenchmarkIntegrations } from './use_csp_benchmark_integrations'; jest.mock('./use_csp_benchmark_integrations'); +jest.mock('../../common/api/use_kubebeat_data_view'); describe('', () => { beforeEach(() => { jest.resetAllMocks(); + // Required for the page template to render the benchmarks page + (useKubebeatDataView as jest.Mock).mockImplementation(() => + createReactQueryResponse({ + status: 'success', + data: createStubDataView({ + spec: { + id: CSP_KUBEBEAT_INDEX_PATTERN, + }, + }), + }) + ); }); const renderBenchmarks = ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx index 56d43816cf34d..e85eb6d6a3570 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx @@ -4,10 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiPageHeaderProps, EuiButton } from '@elastic/eui'; +import { EuiPageHeaderProps, EuiButton, EuiSpacer } from '@elastic/eui'; import React from 'react'; import { allNavigationItems } from '../../common/navigation/constants'; import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; +import { CspLoadingState } from '../../components/csp_loading_state'; import { CspPageTemplate } from '../../components/page_template'; import { BenchmarksTable } from './benchmarks_table'; import { ADD_A_CIS_INTEGRATION, BENCHMARK_INTEGRATIONS, LOADING_BENCHMARKS } from './translations'; @@ -35,11 +36,13 @@ export const Benchmarks = () => { const query = useCspBenchmarkIntegrations(); return ( - + + {query.status === 'loading' && ( + <> + + {LOADING_BENCHMARKS} + + )} {query.status === 'error' && } {query.status === 'success' && ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx index 8678993a73803..54f1ecf9f31ed 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx @@ -7,49 +7,31 @@ import React from 'react'; import type { UseQueryResult } from 'react-query'; import { render, screen } from '@testing-library/react'; +import { useKubebeatDataView } from '../../common/api/use_kubebeat_data_view'; import { Findings } from './findings'; -import { MISSING_KUBEBEAT } from './translations'; import { TestProvider } from '../../test/test_provider'; -import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; +import { + dataPluginMock, + type Start as DataPluginStart, +} from '../../../../../../src/plugins/data/public/mocks'; import { createStubDataView } from '../../../../../../src/plugins/data_views/public/data_views/data_view.stub'; -import { useKubebeatDataView } from './utils'; import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants'; import * as TEST_SUBJECTS from './test_subjects'; import type { DataView } from '../../../../../../src/plugins/data/common'; -jest.mock('./utils'); +jest.mock('../../common/api/use_kubebeat_data_view'); beforeEach(() => { jest.restoreAllMocks(); }); -const Wrapper = ({ data = dataPluginMock.createStartContract() }) => ( +const Wrapper = ({ data = dataPluginMock.createStartContract() }: { data: DataPluginStart }) => ( ); describe('', () => { - it("renders the error state component when 'kubebeat' DataView doesn't exists", async () => { - (useKubebeatDataView as jest.Mock).mockReturnValue({ - status: 'success', - } as UseQueryResult); - - render(); - - expect(await screen.findByText(MISSING_KUBEBEAT)).toBeInTheDocument(); - }); - - it("renders the error state component when 'kubebeat' request status is 'error'", async () => { - (useKubebeatDataView as jest.Mock).mockReturnValue({ - status: 'error', - } as UseQueryResult); - - render(); - - expect(await screen.findByText(MISSING_KUBEBEAT)).toBeInTheDocument(); - }); - it("renders the success state component when 'kubebeat' DataView exists and request status is 'success'", async () => { const data = dataPluginMock.createStartContract(); const source = await data.search.searchSource.create(); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx index 801632d4915bd..6ed043361b44f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx @@ -5,15 +5,13 @@ * 2.0. */ import React from 'react'; -import { EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; import type { EuiPageHeaderProps } from '@elastic/eui'; +import { useKubebeatDataView } from '../../common/api/use_kubebeat_data_view'; import { allNavigationItems } from '../../common/navigation/constants'; import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; import { FindingsContainer } from './findings_container'; import { CspPageTemplate } from '../../components/page_template'; -import { useKubebeatDataView } from './utils'; -import * as TEST_SUBJECTS from './test_subjects'; -import { FINDINGS, MISSING_KUBEBEAT } from './translations'; +import { FINDINGS } from './translations'; const pageHeader: EuiPageHeaderProps = { pageTitle: FINDINGS, @@ -24,27 +22,11 @@ export const Findings = () => { useCspBreadcrumbs([allNavigationItems.findings]); return ( + // `CspPageTemplate` takes care of loading and error states for the kubebeat data view, no need to handle them here - {dataView.status === 'loading' && } - {(dataView.status === 'error' || (dataView.status !== 'loading' && !dataView.data)) && ( - - )} {dataView.status === 'success' && dataView.data && ( )} ); }; - -const LoadingPrompt = () => } />; - -// TODO: follow https://elastic.github.io/eui/#/display/empty-prompt/guidelines -const ErrorPrompt = () => ( - {MISSING_KUBEBEAT}
} - /> -); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/test_subjects.ts index 06c1888a09122..51bcafd46060d 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/test_subjects.ts @@ -8,5 +8,4 @@ export const FINDINGS_SEARCH_BAR = 'findings_search_bar'; export const FINDINGS_TABLE = 'findings_table'; export const FINDINGS_CONTAINER = 'findings_container'; -export const FINDINGS_MISSING_INDEX = 'findings_page_missing_dataview'; export const FINDINGS_TABLE_ZERO_STATE = 'findings_table_zero_state'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts index 0205a6ee0e68d..3517589a37a58 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/translations.ts @@ -30,10 +30,6 @@ export const FINDINGS = i18n.translate('xpack.csp.findings', { defaultMessage: 'Findings', }); -export const MISSING_KUBEBEAT = i18n.translate('xpack.csp.kubebeatDataViewIsMissing', { - defaultMessage: 'Kubebeat DataView is missing', -}); - export const RESOURCE = i18n.translate('xpack.csp.resource', { defaultMessage: 'Resource', }); diff --git a/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx b/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx index 1bb04128d4a67..3806dcb955587 100755 --- a/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx +++ b/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx @@ -10,18 +10,19 @@ import { I18nProvider } from '@kbn/i18n-react'; import { Router, Switch, Route } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from 'react-query'; import { coreMock } from '../../../../../src/core/public/mocks'; +import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import type { CspAppDeps } from '../application/app'; export const TestProvider: React.FC> = ({ core = coreMock.createStart(), - deps = {}, + deps = { data: dataPluginMock.createStartContract() }, params = coreMock.createAppMountParameters(), children, } = {}) => { const queryClient = useMemo(() => new QueryClient(), []); return ( - + From 0a06242647ef2c69a32865b69dd1b48dc24101d3 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Tue, 8 Mar 2022 14:37:43 +0100 Subject: [PATCH 057/140] removes flakiness from prebuilt rules (#127137) --- .../detection_rules/prebuilt_rules.spec.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts index ea83b66ffb95d..0d670a447e9db 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts @@ -18,7 +18,6 @@ import { } from '../../screens/alerts_detection_rules'; import { - changeRowsPerPageTo100, deleteFirstRule, deleteSelectedRules, loadPrebuiltDetectionRules, @@ -97,8 +96,6 @@ describe('Actions with prebuilt rules', () => { }); it('Does not allow to delete one rule when more than one is selected', () => { - changeRowsPerPageTo100(); - const numberOfRulesToBeSelected = 2; selectNumberOfRules(numberOfRulesToBeSelected); @@ -108,14 +105,10 @@ describe('Actions with prebuilt rules', () => { }); it('Deletes and recovers one rule', () => { - changeRowsPerPageTo100(); - const expectedNumberOfRulesAfterDeletion = totalNumberOfPrebuiltRules - 1; const expectedNumberOfRulesAfterRecovering = totalNumberOfPrebuiltRules; deleteFirstRule(); - cy.reload(); - changeRowsPerPageTo100(); cy.get(ELASTIC_RULES_BTN).should( 'have.text', @@ -128,9 +121,6 @@ describe('Actions with prebuilt rules', () => { cy.get(RELOAD_PREBUILT_RULES_BTN).should('not.exist'); - cy.reload(); - changeRowsPerPageTo100(); - cy.get(ELASTIC_RULES_BTN).should( 'have.text', `Elastic rules (${expectedNumberOfRulesAfterRecovering})` @@ -138,16 +128,12 @@ describe('Actions with prebuilt rules', () => { }); it('Deletes and recovers more than one rule', () => { - changeRowsPerPageTo100(); - const numberOfRulesToBeSelected = 2; const expectedNumberOfRulesAfterDeletion = totalNumberOfPrebuiltRules - 2; const expectedNumberOfRulesAfterRecovering = totalNumberOfPrebuiltRules; selectNumberOfRules(numberOfRulesToBeSelected); deleteSelectedRules(); - cy.reload(); - changeRowsPerPageTo100(); cy.get(RELOAD_PREBUILT_RULES_BTN).should('exist'); cy.get(RELOAD_PREBUILT_RULES_BTN).should( @@ -163,9 +149,6 @@ describe('Actions with prebuilt rules', () => { cy.get(RELOAD_PREBUILT_RULES_BTN).should('not.exist'); - cy.reload(); - changeRowsPerPageTo100(); - cy.get(ELASTIC_RULES_BTN).should( 'have.text', `Elastic rules (${expectedNumberOfRulesAfterRecovering})` From 1f514d7c00832e47eef99f2a380f7bfebef0d0f2 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 8 Mar 2022 05:42:06 -0800 Subject: [PATCH 058/140] [eslint] automatically determine dev packages (#127089) * [eslint] automatically determine dev packages * remove old eslint overrides that are not needed anymore --- .eslintrc.js | 30 +++++++++---------- packages/kbn-babel-code-parser/package.json | 3 ++ packages/kbn-optimizer/package.json | 5 +++- packages/kbn-plugin-generator/package.json | 5 +++- .../src/enzyme_helpers.tsx | 1 - .../src/find_test_subject.ts | 1 - packages/kbn-test-jest-helpers/src/random.ts | 1 - .../src/testbed/mount_component.tsx | 1 - .../src/testbed/testbed.ts | 1 - .../src/testbed/types.ts | 1 - packages/kbn-type-summarizer/package.json | 5 +++- .../src/tsd_tests/test_d/method_keys_of.ts | 1 - .../src/tsd_tests/test_d/public_contract.ts | 1 - .../src/tsd_tests/test_d/public_keys.ts | 1 - .../src/tsd_tests/test_d/public_methods_of.ts | 1 - .../src/tsd_tests/test_d/shallow_promise.ts | 1 - .../tsd_tests/test_d/union_to_intersection.ts | 1 - .../src/tsd_tests/test_d/unwrap_observable.ts | 1 - .../src/tsd_tests/test_d/values.ts | 1 - .../src/tsd_tests/test_d/writable.ts | 1 - 20 files changed, 29 insertions(+), 34 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index fc6d6201d1fc0..eb15ce386c864 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,6 +6,11 @@ * Side Public License, v 1. */ +const Path = require('path'); +const Fs = require('fs'); + +const globby = require('globby'); + const APACHE_2_0_LICENSE_HEADER = ` /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -89,23 +94,16 @@ const SAFER_LODASH_SET_DEFINITELYTYPED_HEADER = ` */ `; +const packagePkgJsons = globby.sync('*/package.json', { + cwd: Path.resolve(__dirname, 'packages'), + absolute: true, +}); + /** Packages which should not be included within production code. */ -const DEV_PACKAGES = [ - 'kbn-babel-code-parser', - 'kbn-dev-utils', - 'kbn-cli-dev-mode', - 'kbn-docs-utils', - 'kbn-es*', - 'kbn-eslint*', - 'kbn-optimizer', - 'kbn-plugin-generator', - 'kbn-plugin-helpers', - 'kbn-pm', - 'kbn-storybook', - 'kbn-telemetry-tools', - 'kbn-test', - 'kbn-type-summarizer', -]; +const DEV_PACKAGES = packagePkgJsons.flatMap((path) => { + const pkg = JSON.parse(Fs.readFileSync(path, 'utf8')); + return pkg.kibana && pkg.kibana.devOnly ? Path.dirname(Path.basename(path)) : []; +}); /** Directories (at any depth) which include dev-only code. */ const DEV_DIRECTORIES = [ diff --git a/packages/kbn-babel-code-parser/package.json b/packages/kbn-babel-code-parser/package.json index 7018cb3f8815f..7ff913087f5bd 100755 --- a/packages/kbn-babel-code-parser/package.json +++ b/packages/kbn-babel-code-parser/package.json @@ -8,5 +8,8 @@ "repository": { "type": "git", "url": "https://github.com/elastic/kibana/tree/main/packages/kbn-babel-code-parser" + }, + "kibana": { + "devOnly": true } } diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index a7d8a50927634..4d3d17c5242c6 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -3,5 +3,8 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target_node/index.js" + "main": "./target_node/index.js", + "kibana": { + "devOnly": true + } } \ No newline at end of file diff --git a/packages/kbn-plugin-generator/package.json b/packages/kbn-plugin-generator/package.json index 28b7e849ab3c1..0b6c11709b90e 100644 --- a/packages/kbn-plugin-generator/package.json +++ b/packages/kbn-plugin-generator/package.json @@ -3,5 +3,8 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "target_node/index.js" + "main": "target_node/index.js", + "kibana": { + "devOnly": true + } } \ No newline at end of file diff --git a/packages/kbn-test-jest-helpers/src/enzyme_helpers.tsx b/packages/kbn-test-jest-helpers/src/enzyme_helpers.tsx index 222689d621b5d..8388ed55eb514 100644 --- a/packages/kbn-test-jest-helpers/src/enzyme_helpers.tsx +++ b/packages/kbn-test-jest-helpers/src/enzyme_helpers.tsx @@ -14,7 +14,6 @@ */ import { I18nProvider, InjectedIntl, intlShape, __IntlProvider } from '@kbn/i18n-react'; -// eslint-disable-next-line import/no-extraneous-dependencies import { mount, ReactWrapper, render, shallow } from 'enzyme'; import React, { ReactElement, ValidationMap } from 'react'; import { act as reactAct } from 'react-dom/test-utils'; diff --git a/packages/kbn-test-jest-helpers/src/find_test_subject.ts b/packages/kbn-test-jest-helpers/src/find_test_subject.ts index 9d519f5197cd7..ef3a744fbd99c 100644 --- a/packages/kbn-test-jest-helpers/src/find_test_subject.ts +++ b/packages/kbn-test-jest-helpers/src/find_test_subject.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { ReactWrapper } from 'enzyme'; type Matcher = '=' | '~=' | '|=' | '^=' | '$=' | '*='; diff --git a/packages/kbn-test-jest-helpers/src/random.ts b/packages/kbn-test-jest-helpers/src/random.ts index 9f4efccf810f8..4aa8a30555e0c 100644 --- a/packages/kbn-test-jest-helpers/src/random.ts +++ b/packages/kbn-test-jest-helpers/src/random.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import Chance from 'chance'; const chance = new Chance(); diff --git a/packages/kbn-test-jest-helpers/src/testbed/mount_component.tsx b/packages/kbn-test-jest-helpers/src/testbed/mount_component.tsx index 5c5fd3f2237d1..2ac482abc0fb2 100644 --- a/packages/kbn-test-jest-helpers/src/testbed/mount_component.tsx +++ b/packages/kbn-test-jest-helpers/src/testbed/mount_component.tsx @@ -8,7 +8,6 @@ import React, { ComponentType } from 'react'; import { Store } from 'redux'; -// eslint-disable-next-line import/no-extraneous-dependencies import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; diff --git a/packages/kbn-test-jest-helpers/src/testbed/testbed.ts b/packages/kbn-test-jest-helpers/src/testbed/testbed.ts index 87efb9e61b345..ddd574ace64b8 100644 --- a/packages/kbn-test-jest-helpers/src/testbed/testbed.ts +++ b/packages/kbn-test-jest-helpers/src/testbed/testbed.ts @@ -7,7 +7,6 @@ */ import { Component as ReactComponent } from 'react'; -// eslint-disable-next-line import/no-extraneous-dependencies import { ComponentType, HTMLAttributes, ReactWrapper } from 'enzyme'; import { findTestSubject } from '../find_test_subject'; diff --git a/packages/kbn-test-jest-helpers/src/testbed/types.ts b/packages/kbn-test-jest-helpers/src/testbed/types.ts index ff548f3af5f58..11f8c802a9751 100644 --- a/packages/kbn-test-jest-helpers/src/testbed/types.ts +++ b/packages/kbn-test-jest-helpers/src/testbed/types.ts @@ -7,7 +7,6 @@ */ import { Store } from 'redux'; -// eslint-disable-next-line import/no-extraneous-dependencies import { ReactWrapper as GenericReactWrapper } from 'enzyme'; import { LocationDescriptor } from 'history'; diff --git a/packages/kbn-type-summarizer/package.json b/packages/kbn-type-summarizer/package.json index 531928ce78842..df9c5564d561e 100644 --- a/packages/kbn-type-summarizer/package.json +++ b/packages/kbn-type-summarizer/package.json @@ -3,5 +3,8 @@ "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", "main": "./target_node/index.js", - "private": true + "private": true, + "kibana": { + "devOnly": true + } } diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/method_keys_of.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/method_keys_of.ts index 8438fd1a41dac..816a504b816e4 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/method_keys_of.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/method_keys_of.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectType } from 'tsd'; import { MethodKeysOf } from '../..'; diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/public_contract.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/public_contract.ts index f0b5507a6f63d..b37814255099b 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/public_contract.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/public_contract.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectType } from 'tsd'; import { PublicContract } from '../..'; diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/public_keys.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/public_keys.ts index 1916c7c0b7a02..7f05b30f4c555 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/public_keys.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/public_keys.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectType } from 'tsd'; import { PublicKeys } from '../..'; diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/public_methods_of.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/public_methods_of.ts index fc2626179e7c8..d265b86ece783 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/public_methods_of.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/public_methods_of.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectAssignable, expectNotAssignable } from 'tsd'; import { PublicMethodsOf } from '../..'; diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/shallow_promise.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/shallow_promise.ts index 4bfd5b1826fba..a77558ae6ce9e 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/shallow_promise.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/shallow_promise.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectType } from 'tsd'; import { ShallowPromise } from '../..'; diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/union_to_intersection.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/union_to_intersection.ts index 776da8838ef52..057a2d5e31365 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/union_to_intersection.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/union_to_intersection.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectAssignable } from 'tsd'; import { UnionToIntersection } from '../..'; diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/unwrap_observable.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/unwrap_observable.ts index 6f76fc7cf40fb..5cde15f2ab214 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/unwrap_observable.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/unwrap_observable.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectAssignable } from 'tsd'; import { UnwrapObservable, ObservableLike } from '../..'; diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/values.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/values.ts index 5e9e0d73f5b91..4033c22ea73a7 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/values.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/values.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectAssignable } from 'tsd'; import { Values } from '../..'; diff --git a/packages/kbn-utility-types/src/tsd_tests/test_d/writable.ts b/packages/kbn-utility-types/src/tsd_tests/test_d/writable.ts index db3f6460d1b32..8cfb010d0087c 100644 --- a/packages/kbn-utility-types/src/tsd_tests/test_d/writable.ts +++ b/packages/kbn-utility-types/src/tsd_tests/test_d/writable.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { expectAssignable } from 'tsd'; import { Writable } from '../..'; From 015be5f40ab9c1c0d2effef0d71da3b09dcce07c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Mar 2022 09:29:34 -0500 Subject: [PATCH 059/140] Update babel (main) (#126871) Co-authored-by: Renovate Bot Co-authored-by: Spencer --- package.json | 14 +- packages/kbn-pm/dist/index.js | 2805 ++++----------------------------- yarn.lock | 1094 ++----------- 3 files changed, 447 insertions(+), 3466 deletions(-) diff --git a/package.json b/package.json index baf1103a8ef5c..f3900fdadb40c 100644 --- a/package.json +++ b/package.json @@ -424,16 +424,16 @@ }, "devDependencies": { "@apidevtools/swagger-parser": "^10.0.3", - "@babel/cli": "^7.17.0", - "@babel/core": "^7.17.2", + "@babel/cli": "^7.17.6", + "@babel/core": "^7.17.5", "@babel/eslint-parser": "^7.17.0", "@babel/eslint-plugin": "^7.16.5", - "@babel/generator": "^7.17.0", - "@babel/parser": "^7.17.0", + "@babel/generator": "^7.17.3", + "@babel/parser": "^7.17.3", "@babel/plugin-proposal-class-properties": "^7.16.7", "@babel/plugin-proposal-export-namespace-from": "^7.16.7", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.17.3", "@babel/plugin-proposal-optional-chaining": "^7.16.7", "@babel/plugin-proposal-private-methods": "^7.16.11", "@babel/plugin-transform-runtime": "^7.17.0", @@ -441,7 +441,7 @@ "@babel/preset-react": "^7.16.7", "@babel/preset-typescript": "^7.16.7", "@babel/register": "^7.17.0", - "@babel/traverse": "^7.17.0", + "@babel/traverse": "^7.17.3", "@babel/types": "^7.17.0", "@bazel/ibazel": "^0.15.10", "@bazel/typescript": "4.0.0", @@ -731,7 +731,7 @@ "babel-plugin-add-module-exports": "^1.0.4", "babel-plugin-istanbul": "^6.1.1", "babel-plugin-require-context-hook": "^1.0.0", - "babel-plugin-styled-components": "^2.0.2", + "babel-plugin-styled-components": "^2.0.6", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "backport": "^7.3.1", "callsites": "^3.1.0", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index d22f3add0e2f4..508afa9e9e63a 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -31419,7 +31419,7 @@ function getChalk(options) { } function highlight(code, options = {}) { - if (shouldHighlight(options)) { + if (code !== "" && shouldHighlight(options)) { const chalk = getChalk(options); const defs = getDefs(chalk); return highlightTokens(defs, code); @@ -31467,16 +31467,16 @@ exports.matchToToken = function(match) { Object.defineProperty(exports, "__esModule", { value: true }); -Object.defineProperty(exports, "isIdentifierName", { +Object.defineProperty(exports, "isIdentifierChar", { enumerable: true, get: function () { - return _identifier.isIdentifierName; + return _identifier.isIdentifierChar; } }); -Object.defineProperty(exports, "isIdentifierChar", { +Object.defineProperty(exports, "isIdentifierName", { enumerable: true, get: function () { - return _identifier.isIdentifierChar; + return _identifier.isIdentifierName; } }); Object.defineProperty(exports, "isIdentifierStart", { @@ -31485,6 +31485,12 @@ Object.defineProperty(exports, "isIdentifierStart", { return _identifier.isIdentifierStart; } }); +Object.defineProperty(exports, "isKeyword", { + enumerable: true, + get: function () { + return _keyword.isKeyword; + } +}); Object.defineProperty(exports, "isReservedWord", { enumerable: true, get: function () { @@ -31509,12 +31515,6 @@ Object.defineProperty(exports, "isStrictReservedWord", { return _keyword.isStrictReservedWord; } }); -Object.defineProperty(exports, "isKeyword", { - enumerable: true, - get: function () { - return _keyword.isKeyword; - } -}); var _identifier = __webpack_require__(354); @@ -31530,9 +31530,9 @@ var _keyword = __webpack_require__(355); Object.defineProperty(exports, "__esModule", { value: true }); -exports.isIdentifierStart = isIdentifierStart; exports.isIdentifierChar = isIdentifierChar; exports.isIdentifierName = isIdentifierName; +exports.isIdentifierStart = isIdentifierStart; let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u0870-\u0887\u0889-\u088e\u08a0-\u08c9\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c5d\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cdd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u1711\u171f-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4c\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7ca\ua7d0\ua7d1\ua7d3\ua7d5-\ua7d9\ua7f2-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0898-\u089f\u08ca-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3c\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u180f-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf-\u1ace\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); @@ -31620,11 +31620,11 @@ function isIdentifierName(name) { Object.defineProperty(exports, "__esModule", { value: true }); +exports.isKeyword = isKeyword; exports.isReservedWord = isReservedWord; -exports.isStrictReservedWord = isStrictReservedWord; exports.isStrictBindOnlyReservedWord = isStrictBindOnlyReservedWord; exports.isStrictBindReservedWord = isStrictBindReservedWord; -exports.isKeyword = isKeyword; +exports.isStrictReservedWord = isStrictReservedWord; const reservedWords = { keyword: ["break", "case", "catch", "continue", "debugger", "default", "do", "else", "finally", "for", "function", "if", "return", "switch", "throw", "try", "var", "const", "while", "with", "new", "this", "super", "class", "extends", "export", "import", "null", "true", "false", "in", "instanceof", "typeof", "void", "delete"], strict: ["implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"], @@ -62222,7 +62222,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(564); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildBazelProductionProjects"]; }); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(811); +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(805); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); /* @@ -62248,7 +62248,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(globby__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(811); +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(805); /* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); @@ -88900,8 +88900,8 @@ const arrayUnion = __webpack_require__(242); const merge2 = __webpack_require__(243); const fastGlob = __webpack_require__(778); const dirGlob = __webpack_require__(326); -const gitignore = __webpack_require__(809); -const {FilterStream, UniqueStream} = __webpack_require__(810); +const gitignore = __webpack_require__(803); +const {FilterStream, UniqueStream} = __webpack_require__(804); const DEFAULT_FILTER = () => false; @@ -89084,10 +89084,10 @@ module.exports.gitignore = gitignore; "use strict"; const taskManager = __webpack_require__(779); -const async_1 = __webpack_require__(795); -const stream_1 = __webpack_require__(805); -const sync_1 = __webpack_require__(806); -const settings_1 = __webpack_require__(808); +const async_1 = __webpack_require__(789); +const stream_1 = __webpack_require__(799); +const sync_1 = __webpack_require__(800); +const settings_1 = __webpack_require__(802); const utils = __webpack_require__(780); async function FastGlob(source, options) { assertPatternsInput(source); @@ -89256,9 +89256,9 @@ const path = __webpack_require__(784); exports.path = path; const pattern = __webpack_require__(785); exports.pattern = pattern; -const stream = __webpack_require__(793); +const stream = __webpack_require__(787); exports.stream = stream; -const string = __webpack_require__(794); +const string = __webpack_require__(788); exports.string = string; @@ -89544,8 +89544,8 @@ exports.matchAny = matchAny; const util = __webpack_require__(113); const braces = __webpack_require__(269); -const picomatch = __webpack_require__(787); -const utils = __webpack_require__(790); +const picomatch = __webpack_require__(279); +const utils = __webpack_require__(282); const isEmptyString = val => val === '' || val === './'; /** @@ -90015,8 +90015,22 @@ module.exports = micromatch; "use strict"; - -module.exports = __webpack_require__(788); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.merge = void 0; +const merge2 = __webpack_require__(243); +function merge(streams) { + const mergedStream = merge2(streams); + streams.forEach((stream) => { + stream.once('error', (error) => mergedStream.emit('error', error)); + }); + mergedStream.once('close', () => propagateCloseEventToSources(streams)); + mergedStream.once('end', () => propagateCloseEventToSources(streams)); + return mergedStream; +} +exports.merge = merge; +function propagateCloseEventToSources(streams) { + streams.forEach((stream) => stream.emit('close')); +} /***/ }), @@ -90025,2448 +90039,329 @@ module.exports = __webpack_require__(788); "use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEmpty = exports.isString = void 0; +function isString(input) { + return typeof input === 'string'; +} +exports.isString = isString; +function isEmpty(input) { + return input === ''; +} +exports.isEmpty = isEmpty; -const path = __webpack_require__(4); -const scan = __webpack_require__(789); -const parse = __webpack_require__(792); -const utils = __webpack_require__(790); -const constants = __webpack_require__(791); -const isObject = val => val && typeof val === 'object' && !Array.isArray(val); -/** - * Creates a matcher function from one or more glob patterns. The - * returned function takes a string to match as its first argument, - * and returns true if the string is a match. The returned matcher - * function also takes a boolean as the second argument that, when true, - * returns an object with additional information. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch(glob[, options]); - * - * const isMatch = picomatch('*.!(*a)'); - * console.log(isMatch('a.a')); //=> false - * console.log(isMatch('a.b')); //=> true - * ``` - * @name picomatch - * @param {String|Array} `globs` One or more glob patterns. - * @param {Object=} `options` - * @return {Function=} Returns a matcher function. - * @api public - */ +/***/ }), +/* 789 */ +/***/ (function(module, exports, __webpack_require__) { -const picomatch = (glob, options, returnState = false) => { - if (Array.isArray(glob)) { - const fns = glob.map(input => picomatch(input, options, returnState)); - const arrayMatcher = str => { - for (const isMatch of fns) { - const state = isMatch(str); - if (state) return state; - } - return false; - }; - return arrayMatcher; - } +"use strict"; - const isState = isObject(glob) && glob.tokens && glob.input; +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(790); +const provider_1 = __webpack_require__(792); +class ProviderAsync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = []; + return new Promise((resolve, reject) => { + const stream = this.api(root, task, options); + stream.once('error', reject); + stream.on('data', (entry) => entries.push(options.transform(entry))); + stream.once('end', () => resolve(entries)); + }); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderAsync; - if (glob === '' || (typeof glob !== 'string' && !isState)) { - throw new TypeError('Expected pattern to be a non-empty string'); - } - const opts = options || {}; - const posix = utils.isWindows(options); - const regex = isState - ? picomatch.compileRe(glob, options) - : picomatch.makeRe(glob, options, false, true); +/***/ }), +/* 790 */ +/***/ (function(module, exports, __webpack_require__) { - const state = regex.state; - delete regex.state; +"use strict"; - let isIgnored = () => false; - if (opts.ignore) { - const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; - isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); - } +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(173); +const fsStat = __webpack_require__(289); +const fsWalk = __webpack_require__(294); +const reader_1 = __webpack_require__(791); +class ReaderStream extends reader_1.default { + constructor() { + super(...arguments); + this._walkStream = fsWalk.walkStream; + this._stat = fsStat.stat; + } + dynamic(root, options) { + return this._walkStream(root, options); + } + static(patterns, options) { + const filepaths = patterns.map(this._getFullEntryPath, this); + const stream = new stream_1.PassThrough({ objectMode: true }); + stream._write = (index, _enc, done) => { + return this._getEntry(filepaths[index], patterns[index], options) + .then((entry) => { + if (entry !== null && options.entryFilter(entry)) { + stream.push(entry); + } + if (index === filepaths.length - 1) { + stream.end(); + } + done(); + }) + .catch(done); + }; + for (let i = 0; i < filepaths.length; i++) { + stream.write(i); + } + return stream; + } + _getEntry(filepath, pattern, options) { + return this._getStat(filepath) + .then((stats) => this._makeEntry(stats, pattern)) + .catch((error) => { + if (options.errorFilter(error)) { + return null; + } + throw error; + }); + } + _getStat(filepath) { + return new Promise((resolve, reject) => { + this._stat(filepath, this._fsStatSettings, (error, stats) => { + return error === null ? resolve(stats) : reject(error); + }); + }); + } +} +exports.default = ReaderStream; - const matcher = (input, returnObject = false) => { - const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); - const result = { glob, state, regex, posix, input, output, match, isMatch }; - if (typeof opts.onResult === 'function') { - opts.onResult(result); - } +/***/ }), +/* 791 */ +/***/ (function(module, exports, __webpack_require__) { - if (isMatch === false) { - result.isMatch = false; - return returnObject ? result : false; - } +"use strict"; - if (isIgnored(input)) { - if (typeof opts.onIgnore === 'function') { - opts.onIgnore(result); - } - result.isMatch = false; - return returnObject ? result : false; +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsStat = __webpack_require__(289); +const utils = __webpack_require__(780); +class Reader { + constructor(_settings) { + this._settings = _settings; + this._fsStatSettings = new fsStat.Settings({ + followSymbolicLink: this._settings.followSymbolicLinks, + fs: this._settings.fs, + throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks + }); } - - if (typeof opts.onMatch === 'function') { - opts.onMatch(result); + _getFullEntryPath(filepath) { + return path.resolve(this._settings.cwd, filepath); } - return returnObject ? result : true; - }; + _makeEntry(stats, pattern) { + const entry = { + name: pattern, + path: pattern, + dirent: utils.fs.createDirentFromStats(pattern, stats) + }; + if (this._settings.stats) { + entry.stats = stats; + } + return entry; + } + _isFatalError(error) { + return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; + } +} +exports.default = Reader; - if (returnState) { - matcher.state = state; - } - return matcher; -}; +/***/ }), +/* 792 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Test `input` with the given `regex`. This is used by the main - * `picomatch()` function to test the input string. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.test(input, regex[, options]); - * - * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/)); - * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' } - * ``` - * @param {String} `input` String to test. - * @param {RegExp} `regex` - * @return {Object} Returns an object with matching info. - * @api public - */ +"use strict"; -picomatch.test = (input, regex, options, { glob, posix } = {}) => { - if (typeof input !== 'string') { - throw new TypeError('Expected input to be a string'); - } +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const deep_1 = __webpack_require__(793); +const entry_1 = __webpack_require__(796); +const error_1 = __webpack_require__(797); +const entry_2 = __webpack_require__(798); +class Provider { + constructor(_settings) { + this._settings = _settings; + this.errorFilter = new error_1.default(this._settings); + this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); + this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); + this.entryTransformer = new entry_2.default(this._settings); + } + _getRootDirectory(task) { + return path.resolve(this._settings.cwd, task.base); + } + _getReaderOptions(task) { + const basePath = task.base === '.' ? '' : task.base; + return { + basePath, + pathSegmentSeparator: '/', + concurrency: this._settings.concurrency, + deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), + entryFilter: this.entryFilter.getFilter(task.positive, task.negative), + errorFilter: this.errorFilter.getFilter(), + followSymbolicLinks: this._settings.followSymbolicLinks, + fs: this._settings.fs, + stats: this._settings.stats, + throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, + transform: this.entryTransformer.getTransformer() + }; + } + _getMicromatchOptions() { + return { + dot: this._settings.dot, + matchBase: this._settings.baseNameMatch, + nobrace: !this._settings.braceExpansion, + nocase: !this._settings.caseSensitiveMatch, + noext: !this._settings.extglob, + noglobstar: !this._settings.globstar, + posix: true, + strictSlashes: false + }; + } +} +exports.default = Provider; - if (input === '') { - return { isMatch: false, output: '' }; - } - const opts = options || {}; - const format = opts.format || (posix ? utils.toPosixSlashes : null); - let match = input === glob; - let output = (match && format) ? format(input) : input; +/***/ }), +/* 793 */ +/***/ (function(module, exports, __webpack_require__) { - if (match === false) { - output = format ? format(input) : input; - match = output === glob; - } +"use strict"; - if (match === false || opts.capture === true) { - if (opts.matchBase === true || opts.basename === true) { - match = picomatch.matchBase(input, regex, options, posix); - } else { - match = regex.exec(output); +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(780); +const partial_1 = __webpack_require__(794); +class DeepFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; } - } + getFilter(basePath, positive, negative) { + const matcher = this._getMatcher(positive); + const negativeRe = this._getNegativePatternsRe(negative); + return (entry) => this._filter(basePath, entry, matcher, negativeRe); + } + _getMatcher(patterns) { + return new partial_1.default(patterns, this._settings, this._micromatchOptions); + } + _getNegativePatternsRe(patterns) { + const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); + return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); + } + _filter(basePath, entry, matcher, negativeRe) { + if (this._isSkippedByDeep(basePath, entry.path)) { + return false; + } + if (this._isSkippedSymbolicLink(entry)) { + return false; + } + const filepath = utils.path.removeLeadingDotSegment(entry.path); + if (this._isSkippedByPositivePatterns(filepath, matcher)) { + return false; + } + return this._isSkippedByNegativePatterns(filepath, negativeRe); + } + _isSkippedByDeep(basePath, entryPath) { + /** + * Avoid unnecessary depth calculations when it doesn't matter. + */ + if (this._settings.deep === Infinity) { + return false; + } + return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; + } + _getEntryLevel(basePath, entryPath) { + const entryPathDepth = entryPath.split('/').length; + if (basePath === '') { + return entryPathDepth; + } + const basePathDepth = basePath.split('/').length; + return entryPathDepth - basePathDepth; + } + _isSkippedSymbolicLink(entry) { + return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); + } + _isSkippedByPositivePatterns(entryPath, matcher) { + return !this._settings.baseNameMatch && !matcher.match(entryPath); + } + _isSkippedByNegativePatterns(entryPath, patternsRe) { + return !utils.pattern.matchAny(entryPath, patternsRe); + } +} +exports.default = DeepFilter; - return { isMatch: Boolean(match), match, output }; -}; -/** - * Match the basename of a filepath. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.matchBase(input, glob[, options]); - * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true - * ``` - * @param {String} `input` String to test. - * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe). - * @return {Boolean} - * @api public - */ +/***/ }), +/* 794 */ +/***/ (function(module, exports, __webpack_require__) { -picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { - const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); - return regex.test(path.basename(input)); -}; +"use strict"; -/** - * Returns true if **any** of the given glob `patterns` match the specified `string`. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.isMatch(string, patterns[, options]); - * - * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true - * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false - * ``` - * @param {String|Array} str The string to test. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} [options] See available [options](#options). - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ +Object.defineProperty(exports, "__esModule", { value: true }); +const matcher_1 = __webpack_require__(795); +class PartialMatcher extends matcher_1.default { + match(filepath) { + const parts = filepath.split('/'); + const levels = parts.length; + const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); + for (const pattern of patterns) { + const section = pattern.sections[0]; + /** + * In this case, the pattern has a globstar and we must read all directories unconditionally, + * but only if the level has reached the end of the first group. + * + * fixtures/{a,b}/** + * ^ true/false ^ always true + */ + if (!pattern.complete && levels > section.length) { + return true; + } + const match = parts.every((part, index) => { + const segment = pattern.segments[index]; + if (segment.dynamic && segment.patternRe.test(part)) { + return true; + } + if (!segment.dynamic && segment.pattern === part) { + return true; + } + return false; + }); + if (match) { + return true; + } + } + return false; + } +} +exports.default = PartialMatcher; -picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); -/** - * Parse a glob pattern to create the source string for a regular - * expression. - * - * ```js - * const picomatch = require('picomatch'); - * const result = picomatch.parse(pattern[, options]); - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} Returns an object with useful properties and output to be used as a regex source string. - * @api public - */ +/***/ }), +/* 795 */ +/***/ (function(module, exports, __webpack_require__) { -picomatch.parse = (pattern, options) => { - if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options)); - return parse(pattern, { ...options, fastpaths: false }); -}; - -/** - * Scan a glob pattern to separate the pattern into segments. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.scan(input[, options]); - * - * const result = picomatch.scan('!./foo/*.js'); - * console.log(result); - * { prefix: '!./', - * input: '!./foo/*.js', - * start: 3, - * base: 'foo', - * glob: '*.js', - * isBrace: false, - * isBracket: false, - * isGlob: true, - * isExtglob: false, - * isGlobstar: false, - * negated: true } - * ``` - * @param {String} `input` Glob pattern to scan. - * @param {Object} `options` - * @return {Object} Returns an object with - * @api public - */ - -picomatch.scan = (input, options) => scan(input, options); - -/** - * Compile a regular expression from the `state` object returned by the - * [parse()](#parse) method. - * - * @param {Object} `state` - * @param {Object} `options` - * @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser. - * @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging. - * @return {RegExp} - * @api public - */ - -picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { - if (returnOutput === true) { - return state.output; - } - - const opts = options || {}; - const prepend = opts.contains ? '' : '^'; - const append = opts.contains ? '' : '$'; - - let source = `${prepend}(?:${state.output})${append}`; - if (state && state.negated === true) { - source = `^(?!${source}).*$`; - } - - const regex = picomatch.toRegex(source, options); - if (returnState === true) { - regex.state = state; - } - - return regex; -}; - -/** - * Create a regular expression from a parsed glob pattern. - * - * ```js - * const picomatch = require('picomatch'); - * const state = picomatch.parse('*.js'); - * // picomatch.compileRe(state[, options]); - * - * console.log(picomatch.compileRe(state)); - * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ - * ``` - * @param {String} `state` The object returned from the `.parse` method. - * @param {Object} `options` - * @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result. - * @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression. - * @return {RegExp} Returns a regex created from the given pattern. - * @api public - */ - -picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { - if (!input || typeof input !== 'string') { - throw new TypeError('Expected a non-empty string'); - } - - let parsed = { negated: false, fastpaths: true }; - - if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { - parsed.output = parse.fastpaths(input, options); - } - - if (!parsed.output) { - parsed = parse(input, options); - } - - return picomatch.compileRe(parsed, options, returnOutput, returnState); -}; - -/** - * Create a regular expression from the given regex source string. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.toRegex(source[, options]); - * - * const { output } = picomatch.parse('*.js'); - * console.log(picomatch.toRegex(output)); - * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ - * ``` - * @param {String} `source` Regular expression source string. - * @param {Object} `options` - * @return {RegExp} - * @api public - */ - -picomatch.toRegex = (source, options) => { - try { - const opts = options || {}; - return new RegExp(source, opts.flags || (opts.nocase ? 'i' : '')); - } catch (err) { - if (options && options.debug === true) throw err; - return /$^/; - } -}; - -/** - * Picomatch constants. - * @return {Object} - */ - -picomatch.constants = constants; - -/** - * Expose "picomatch" - */ - -module.exports = picomatch; - - -/***/ }), -/* 789 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const utils = __webpack_require__(790); -const { - CHAR_ASTERISK, /* * */ - CHAR_AT, /* @ */ - CHAR_BACKWARD_SLASH, /* \ */ - CHAR_COMMA, /* , */ - CHAR_DOT, /* . */ - CHAR_EXCLAMATION_MARK, /* ! */ - CHAR_FORWARD_SLASH, /* / */ - CHAR_LEFT_CURLY_BRACE, /* { */ - CHAR_LEFT_PARENTHESES, /* ( */ - CHAR_LEFT_SQUARE_BRACKET, /* [ */ - CHAR_PLUS, /* + */ - CHAR_QUESTION_MARK, /* ? */ - CHAR_RIGHT_CURLY_BRACE, /* } */ - CHAR_RIGHT_PARENTHESES, /* ) */ - CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(791); - -const isPathSeparator = code => { - return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; -}; - -const depth = token => { - if (token.isPrefix !== true) { - token.depth = token.isGlobstar ? Infinity : 1; - } -}; - -/** - * Quickly scans a glob pattern and returns an object with a handful of - * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), - * `glob` (the actual pattern), `negated` (true if the path starts with `!` but not - * with `!(`) and `negatedExtglob` (true if the path starts with `!(`). - * - * ```js - * const pm = require('picomatch'); - * console.log(pm.scan('foo/bar/*.js')); - * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {Object} Returns an object with tokens and regex source string. - * @api public - */ - -const scan = (input, options) => { - const opts = options || {}; - - const length = input.length - 1; - const scanToEnd = opts.parts === true || opts.scanToEnd === true; - const slashes = []; - const tokens = []; - const parts = []; - - let str = input; - let index = -1; - let start = 0; - let lastIndex = 0; - let isBrace = false; - let isBracket = false; - let isGlob = false; - let isExtglob = false; - let isGlobstar = false; - let braceEscaped = false; - let backslashes = false; - let negated = false; - let negatedExtglob = false; - let finished = false; - let braces = 0; - let prev; - let code; - let token = { value: '', depth: 0, isGlob: false }; - - const eos = () => index >= length; - const peek = () => str.charCodeAt(index + 1); - const advance = () => { - prev = code; - return str.charCodeAt(++index); - }; - - while (index < length) { - code = advance(); - let next; - - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - code = advance(); - - if (code === CHAR_LEFT_CURLY_BRACE) { - braceEscaped = true; - } - continue; - } - - if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { - braces++; - - while (eos() !== true && (code = advance())) { - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - advance(); - continue; - } - - if (code === CHAR_LEFT_CURLY_BRACE) { - braces++; - continue; - } - - if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { - isBrace = token.isBrace = true; - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - - break; - } - - if (braceEscaped !== true && code === CHAR_COMMA) { - isBrace = token.isBrace = true; - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - - break; - } - - if (code === CHAR_RIGHT_CURLY_BRACE) { - braces--; - - if (braces === 0) { - braceEscaped = false; - isBrace = token.isBrace = true; - finished = true; - break; - } - } - } - - if (scanToEnd === true) { - continue; - } - - break; - } - - if (code === CHAR_FORWARD_SLASH) { - slashes.push(index); - tokens.push(token); - token = { value: '', depth: 0, isGlob: false }; - - if (finished === true) continue; - if (prev === CHAR_DOT && index === (start + 1)) { - start += 2; - continue; - } - - lastIndex = index + 1; - continue; - } - - if (opts.noext !== true) { - const isExtglobChar = code === CHAR_PLUS - || code === CHAR_AT - || code === CHAR_ASTERISK - || code === CHAR_QUESTION_MARK - || code === CHAR_EXCLAMATION_MARK; - - if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { - isGlob = token.isGlob = true; - isExtglob = token.isExtglob = true; - finished = true; - if (code === CHAR_EXCLAMATION_MARK && index === start) { - negatedExtglob = true; - } - - if (scanToEnd === true) { - while (eos() !== true && (code = advance())) { - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - code = advance(); - continue; - } - - if (code === CHAR_RIGHT_PARENTHESES) { - isGlob = token.isGlob = true; - finished = true; - break; - } - } - continue; - } - break; - } - } - - if (code === CHAR_ASTERISK) { - if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - break; - } - - if (code === CHAR_QUESTION_MARK) { - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { - continue; - } - break; - } - - if (code === CHAR_LEFT_SQUARE_BRACKET) { - while (eos() !== true && (next = advance())) { - if (next === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - advance(); - continue; - } - - if (next === CHAR_RIGHT_SQUARE_BRACKET) { - isBracket = token.isBracket = true; - isGlob = token.isGlob = true; - finished = true; - break; - } - } - - if (scanToEnd === true) { - continue; - } - - break; - } - - if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { - negated = token.negated = true; - start++; - continue; - } - - if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { - isGlob = token.isGlob = true; - - if (scanToEnd === true) { - while (eos() !== true && (code = advance())) { - if (code === CHAR_LEFT_PARENTHESES) { - backslashes = token.backslashes = true; - code = advance(); - continue; - } - - if (code === CHAR_RIGHT_PARENTHESES) { - finished = true; - break; - } - } - continue; - } - break; - } - - if (isGlob === true) { - finished = true; - - if (scanToEnd === true) { - continue; - } - - break; - } - } - - if (opts.noext === true) { - isExtglob = false; - isGlob = false; - } - - let base = str; - let prefix = ''; - let glob = ''; - - if (start > 0) { - prefix = str.slice(0, start); - str = str.slice(start); - lastIndex -= start; - } - - if (base && isGlob === true && lastIndex > 0) { - base = str.slice(0, lastIndex); - glob = str.slice(lastIndex); - } else if (isGlob === true) { - base = ''; - glob = str; - } else { - base = str; - } - - if (base && base !== '' && base !== '/' && base !== str) { - if (isPathSeparator(base.charCodeAt(base.length - 1))) { - base = base.slice(0, -1); - } - } - - if (opts.unescape === true) { - if (glob) glob = utils.removeBackslashes(glob); - - if (base && backslashes === true) { - base = utils.removeBackslashes(base); - } - } - - const state = { - prefix, - input, - start, - base, - glob, - isBrace, - isBracket, - isGlob, - isExtglob, - isGlobstar, - negated, - negatedExtglob - }; - - if (opts.tokens === true) { - state.maxDepth = 0; - if (!isPathSeparator(code)) { - tokens.push(token); - } - state.tokens = tokens; - } - - if (opts.parts === true || opts.tokens === true) { - let prevIndex; - - for (let idx = 0; idx < slashes.length; idx++) { - const n = prevIndex ? prevIndex + 1 : start; - const i = slashes[idx]; - const value = input.slice(n, i); - if (opts.tokens) { - if (idx === 0 && start !== 0) { - tokens[idx].isPrefix = true; - tokens[idx].value = prefix; - } else { - tokens[idx].value = value; - } - depth(tokens[idx]); - state.maxDepth += tokens[idx].depth; - } - if (idx !== 0 || value !== '') { - parts.push(value); - } - prevIndex = i; - } - - if (prevIndex && prevIndex + 1 < input.length) { - const value = input.slice(prevIndex + 1); - parts.push(value); - - if (opts.tokens) { - tokens[tokens.length - 1].value = value; - depth(tokens[tokens.length - 1]); - state.maxDepth += tokens[tokens.length - 1].depth; - } - } - - state.slashes = slashes; - state.parts = parts; - } - - return state; -}; - -module.exports = scan; - - -/***/ }), -/* 790 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const path = __webpack_require__(4); -const win32 = process.platform === 'win32'; -const { - REGEX_BACKSLASH, - REGEX_REMOVE_BACKSLASH, - REGEX_SPECIAL_CHARS, - REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(791); - -exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); -exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); -exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); -exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); -exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); - -exports.removeBackslashes = str => { - return str.replace(REGEX_REMOVE_BACKSLASH, match => { - return match === '\\' ? '' : match; - }); -}; - -exports.supportsLookbehinds = () => { - const segs = process.version.slice(1).split('.').map(Number); - if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { - return true; - } - return false; -}; - -exports.isWindows = options => { - if (options && typeof options.windows === 'boolean') { - return options.windows; - } - return win32 === true || path.sep === '\\'; -}; - -exports.escapeLast = (input, char, lastIdx) => { - const idx = input.lastIndexOf(char, lastIdx); - if (idx === -1) return input; - if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1); - return `${input.slice(0, idx)}\\${input.slice(idx)}`; -}; - -exports.removePrefix = (input, state = {}) => { - let output = input; - if (output.startsWith('./')) { - output = output.slice(2); - state.prefix = './'; - } - return output; -}; - -exports.wrapOutput = (input, state = {}, options = {}) => { - const prepend = options.contains ? '' : '^'; - const append = options.contains ? '' : '$'; - - let output = `${prepend}(?:${input})${append}`; - if (state.negated === true) { - output = `(?:^(?!${output}).*$)`; - } - return output; -}; - - -/***/ }), -/* 791 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const path = __webpack_require__(4); -const WIN_SLASH = '\\\\/'; -const WIN_NO_SLASH = `[^${WIN_SLASH}]`; - -/** - * Posix glob regex - */ - -const DOT_LITERAL = '\\.'; -const PLUS_LITERAL = '\\+'; -const QMARK_LITERAL = '\\?'; -const SLASH_LITERAL = '\\/'; -const ONE_CHAR = '(?=.)'; -const QMARK = '[^/]'; -const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; -const START_ANCHOR = `(?:^|${SLASH_LITERAL})`; -const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; -const NO_DOT = `(?!${DOT_LITERAL})`; -const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; -const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; -const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; -const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; -const STAR = `${QMARK}*?`; - -const POSIX_CHARS = { - DOT_LITERAL, - PLUS_LITERAL, - QMARK_LITERAL, - SLASH_LITERAL, - ONE_CHAR, - QMARK, - END_ANCHOR, - DOTS_SLASH, - NO_DOT, - NO_DOTS, - NO_DOT_SLASH, - NO_DOTS_SLASH, - QMARK_NO_DOT, - STAR, - START_ANCHOR -}; - -/** - * Windows glob regex - */ - -const WINDOWS_CHARS = { - ...POSIX_CHARS, - - SLASH_LITERAL: `[${WIN_SLASH}]`, - QMARK: WIN_NO_SLASH, - STAR: `${WIN_NO_SLASH}*?`, - DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, - NO_DOT: `(?!${DOT_LITERAL})`, - NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, - NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, - NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, - QMARK_NO_DOT: `[^.${WIN_SLASH}]`, - START_ANCHOR: `(?:^|[${WIN_SLASH}])`, - END_ANCHOR: `(?:[${WIN_SLASH}]|$)` -}; - -/** - * POSIX Bracket Regex - */ - -const POSIX_REGEX_SOURCE = { - alnum: 'a-zA-Z0-9', - alpha: 'a-zA-Z', - ascii: '\\x00-\\x7F', - blank: ' \\t', - cntrl: '\\x00-\\x1F\\x7F', - digit: '0-9', - graph: '\\x21-\\x7E', - lower: 'a-z', - print: '\\x20-\\x7E ', - punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', - space: ' \\t\\r\\n\\v\\f', - upper: 'A-Z', - word: 'A-Za-z0-9_', - xdigit: 'A-Fa-f0-9' -}; - -module.exports = { - MAX_LENGTH: 1024 * 64, - POSIX_REGEX_SOURCE, - - // regular expressions - REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, - REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, - REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, - REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, - REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, - REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, - - // Replace globs with equivalent patterns to reduce parsing time. - REPLACEMENTS: { - '***': '*', - '**/**': '**', - '**/**/**': '**' - }, - - // Digits - CHAR_0: 48, /* 0 */ - CHAR_9: 57, /* 9 */ - - // Alphabet chars. - CHAR_UPPERCASE_A: 65, /* A */ - CHAR_LOWERCASE_A: 97, /* a */ - CHAR_UPPERCASE_Z: 90, /* Z */ - CHAR_LOWERCASE_Z: 122, /* z */ - - CHAR_LEFT_PARENTHESES: 40, /* ( */ - CHAR_RIGHT_PARENTHESES: 41, /* ) */ - - CHAR_ASTERISK: 42, /* * */ - - // Non-alphabetic chars. - CHAR_AMPERSAND: 38, /* & */ - CHAR_AT: 64, /* @ */ - CHAR_BACKWARD_SLASH: 92, /* \ */ - CHAR_CARRIAGE_RETURN: 13, /* \r */ - CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */ - CHAR_COLON: 58, /* : */ - CHAR_COMMA: 44, /* , */ - CHAR_DOT: 46, /* . */ - CHAR_DOUBLE_QUOTE: 34, /* " */ - CHAR_EQUAL: 61, /* = */ - CHAR_EXCLAMATION_MARK: 33, /* ! */ - CHAR_FORM_FEED: 12, /* \f */ - CHAR_FORWARD_SLASH: 47, /* / */ - CHAR_GRAVE_ACCENT: 96, /* ` */ - CHAR_HASH: 35, /* # */ - CHAR_HYPHEN_MINUS: 45, /* - */ - CHAR_LEFT_ANGLE_BRACKET: 60, /* < */ - CHAR_LEFT_CURLY_BRACE: 123, /* { */ - CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */ - CHAR_LINE_FEED: 10, /* \n */ - CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */ - CHAR_PERCENT: 37, /* % */ - CHAR_PLUS: 43, /* + */ - CHAR_QUESTION_MARK: 63, /* ? */ - CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */ - CHAR_RIGHT_CURLY_BRACE: 125, /* } */ - CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */ - CHAR_SEMICOLON: 59, /* ; */ - CHAR_SINGLE_QUOTE: 39, /* ' */ - CHAR_SPACE: 32, /* */ - CHAR_TAB: 9, /* \t */ - CHAR_UNDERSCORE: 95, /* _ */ - CHAR_VERTICAL_LINE: 124, /* | */ - CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */ - - SEP: path.sep, - - /** - * Create EXTGLOB_CHARS - */ - - extglobChars(chars) { - return { - '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` }, - '?': { type: 'qmark', open: '(?:', close: ')?' }, - '+': { type: 'plus', open: '(?:', close: ')+' }, - '*': { type: 'star', open: '(?:', close: ')*' }, - '@': { type: 'at', open: '(?:', close: ')' } - }; - }, - - /** - * Create GLOB_CHARS - */ - - globChars(win32) { - return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; - } -}; - - -/***/ }), -/* 792 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const constants = __webpack_require__(791); -const utils = __webpack_require__(790); - -/** - * Constants - */ - -const { - MAX_LENGTH, - POSIX_REGEX_SOURCE, - REGEX_NON_SPECIAL_CHARS, - REGEX_SPECIAL_CHARS_BACKREF, - REPLACEMENTS -} = constants; - -/** - * Helpers - */ - -const expandRange = (args, options) => { - if (typeof options.expandRange === 'function') { - return options.expandRange(...args, options); - } - - args.sort(); - const value = `[${args.join('-')}]`; - - try { - /* eslint-disable-next-line no-new */ - new RegExp(value); - } catch (ex) { - return args.map(v => utils.escapeRegex(v)).join('..'); - } - - return value; -}; - -/** - * Create the message for a syntax error - */ - -const syntaxError = (type, char) => { - return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; -}; - -/** - * Parse the given input string. - * @param {String} input - * @param {Object} options - * @return {Object} - */ - -const parse = (input, options) => { - if (typeof input !== 'string') { - throw new TypeError('Expected a string'); - } - - input = REPLACEMENTS[input] || input; - - const opts = { ...options }; - const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; - - let len = input.length; - if (len > max) { - throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); - } - - const bos = { type: 'bos', value: '', output: opts.prepend || '' }; - const tokens = [bos]; - - const capture = opts.capture ? '' : '?:'; - const win32 = utils.isWindows(options); - - // create constants based on platform, for windows or posix - const PLATFORM_CHARS = constants.globChars(win32); - const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); - - const { - DOT_LITERAL, - PLUS_LITERAL, - SLASH_LITERAL, - ONE_CHAR, - DOTS_SLASH, - NO_DOT, - NO_DOT_SLASH, - NO_DOTS_SLASH, - QMARK, - QMARK_NO_DOT, - STAR, - START_ANCHOR - } = PLATFORM_CHARS; - - const globstar = opts => { - return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; - }; - - const nodot = opts.dot ? '' : NO_DOT; - const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; - let star = opts.bash === true ? globstar(opts) : STAR; - - if (opts.capture) { - star = `(${star})`; - } - - // minimatch options support - if (typeof opts.noext === 'boolean') { - opts.noextglob = opts.noext; - } - - const state = { - input, - index: -1, - start: 0, - dot: opts.dot === true, - consumed: '', - output: '', - prefix: '', - backtrack: false, - negated: false, - brackets: 0, - braces: 0, - parens: 0, - quotes: 0, - globstar: false, - tokens - }; - - input = utils.removePrefix(input, state); - len = input.length; - - const extglobs = []; - const braces = []; - const stack = []; - let prev = bos; - let value; - - /** - * Tokenizing helpers - */ - - const eos = () => state.index === len - 1; - const peek = state.peek = (n = 1) => input[state.index + n]; - const advance = state.advance = () => input[++state.index] || ''; - const remaining = () => input.slice(state.index + 1); - const consume = (value = '', num = 0) => { - state.consumed += value; - state.index += num; - }; - - const append = token => { - state.output += token.output != null ? token.output : token.value; - consume(token.value); - }; - - const negate = () => { - let count = 1; - - while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) { - advance(); - state.start++; - count++; - } - - if (count % 2 === 0) { - return false; - } - - state.negated = true; - state.start++; - return true; - }; - - const increment = type => { - state[type]++; - stack.push(type); - }; - - const decrement = type => { - state[type]--; - stack.pop(); - }; - - /** - * Push tokens onto the tokens array. This helper speeds up - * tokenizing by 1) helping us avoid backtracking as much as possible, - * and 2) helping us avoid creating extra tokens when consecutive - * characters are plain text. This improves performance and simplifies - * lookbehinds. - */ - - const push = tok => { - if (prev.type === 'globstar') { - const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); - const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren')); - - if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) { - state.output = state.output.slice(0, -prev.output.length); - prev.type = 'star'; - prev.value = '*'; - prev.output = star; - state.output += prev.output; - } - } - - if (extglobs.length && tok.type !== 'paren') { - extglobs[extglobs.length - 1].inner += tok.value; - } - - if (tok.value || tok.output) append(tok); - if (prev && prev.type === 'text' && tok.type === 'text') { - prev.value += tok.value; - prev.output = (prev.output || '') + tok.value; - return; - } - - tok.prev = prev; - tokens.push(tok); - prev = tok; - }; - - const extglobOpen = (type, value) => { - const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; - - token.prev = prev; - token.parens = state.parens; - token.output = state.output; - const output = (opts.capture ? '(' : '') + token.open; - - increment('parens'); - push({ type, value, output: state.output ? '' : ONE_CHAR }); - push({ type: 'paren', extglob: true, value: advance(), output }); - extglobs.push(token); - }; - - const extglobClose = token => { - let output = token.close + (opts.capture ? ')' : ''); - let rest; - - if (token.type === 'negate') { - let extglobStar = star; - - if (token.inner && token.inner.length > 1 && token.inner.includes('/')) { - extglobStar = globstar(opts); - } - - if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { - output = token.close = `)$))${extglobStar}`; - } - - if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { - output = token.close = `)${rest})${extglobStar})`; - } - - if (token.prev.type === 'bos') { - state.negatedExtglob = true; - } - } - - push({ type: 'paren', extglob: true, value, output }); - decrement('parens'); - }; - - /** - * Fast paths - */ - - if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { - let backslashes = false; - - let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { - if (first === '\\') { - backslashes = true; - return m; - } - - if (first === '?') { - if (esc) { - return esc + first + (rest ? QMARK.repeat(rest.length) : ''); - } - if (index === 0) { - return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ''); - } - return QMARK.repeat(chars.length); - } - - if (first === '.') { - return DOT_LITERAL.repeat(chars.length); - } - - if (first === '*') { - if (esc) { - return esc + first + (rest ? star : ''); - } - return star; - } - return esc ? m : `\\${m}`; - }); - - if (backslashes === true) { - if (opts.unescape === true) { - output = output.replace(/\\/g, ''); - } else { - output = output.replace(/\\+/g, m => { - return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : ''); - }); - } - } - - if (output === input && opts.contains === true) { - state.output = input; - return state; - } - - state.output = utils.wrapOutput(output, state, options); - return state; - } - - /** - * Tokenize input until we reach end-of-string - */ - - while (!eos()) { - value = advance(); - - if (value === '\u0000') { - continue; - } - - /** - * Escaped characters - */ - - if (value === '\\') { - const next = peek(); - - if (next === '/' && opts.bash !== true) { - continue; - } - - if (next === '.' || next === ';') { - continue; - } - - if (!next) { - value += '\\'; - push({ type: 'text', value }); - continue; - } - - // collapse slashes to reduce potential for exploits - const match = /^\\+/.exec(remaining()); - let slashes = 0; - - if (match && match[0].length > 2) { - slashes = match[0].length; - state.index += slashes; - if (slashes % 2 !== 0) { - value += '\\'; - } - } - - if (opts.unescape === true) { - value = advance(); - } else { - value += advance(); - } - - if (state.brackets === 0) { - push({ type: 'text', value }); - continue; - } - } - - /** - * If we're inside a regex character class, continue - * until we reach the closing bracket. - */ - - if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) { - if (opts.posix !== false && value === ':') { - const inner = prev.value.slice(1); - if (inner.includes('[')) { - prev.posix = true; - - if (inner.includes(':')) { - const idx = prev.value.lastIndexOf('['); - const pre = prev.value.slice(0, idx); - const rest = prev.value.slice(idx + 2); - const posix = POSIX_REGEX_SOURCE[rest]; - if (posix) { - prev.value = pre + posix; - state.backtrack = true; - advance(); - - if (!bos.output && tokens.indexOf(prev) === 1) { - bos.output = ONE_CHAR; - } - continue; - } - } - } - } - - if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) { - value = `\\${value}`; - } - - if (value === ']' && (prev.value === '[' || prev.value === '[^')) { - value = `\\${value}`; - } - - if (opts.posix === true && value === '!' && prev.value === '[') { - value = '^'; - } - - prev.value += value; - append({ value }); - continue; - } - - /** - * If we're inside a quoted string, continue - * until we reach the closing double quote. - */ - - if (state.quotes === 1 && value !== '"') { - value = utils.escapeRegex(value); - prev.value += value; - append({ value }); - continue; - } - - /** - * Double quotes - */ - - if (value === '"') { - state.quotes = state.quotes === 1 ? 0 : 1; - if (opts.keepQuotes === true) { - push({ type: 'text', value }); - } - continue; - } - - /** - * Parentheses - */ - - if (value === '(') { - increment('parens'); - push({ type: 'paren', value }); - continue; - } - - if (value === ')') { - if (state.parens === 0 && opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('opening', '(')); - } - - const extglob = extglobs[extglobs.length - 1]; - if (extglob && state.parens === extglob.parens + 1) { - extglobClose(extglobs.pop()); - continue; - } - - push({ type: 'paren', value, output: state.parens ? ')' : '\\)' }); - decrement('parens'); - continue; - } - - /** - * Square brackets - */ - - if (value === '[') { - if (opts.nobracket === true || !remaining().includes(']')) { - if (opts.nobracket !== true && opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('closing', ']')); - } - - value = `\\${value}`; - } else { - increment('brackets'); - } - - push({ type: 'bracket', value }); - continue; - } - - if (value === ']') { - if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) { - push({ type: 'text', value, output: `\\${value}` }); - continue; - } - - if (state.brackets === 0) { - if (opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('opening', '[')); - } - - push({ type: 'text', value, output: `\\${value}` }); - continue; - } - - decrement('brackets'); - - const prevValue = prev.value.slice(1); - if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) { - value = `/${value}`; - } - - prev.value += value; - append({ value }); - - // when literal brackets are explicitly disabled - // assume we should match with a regex character class - if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { - continue; - } - - const escaped = utils.escapeRegex(prev.value); - state.output = state.output.slice(0, -prev.value.length); - - // when literal brackets are explicitly enabled - // assume we should escape the brackets to match literal characters - if (opts.literalBrackets === true) { - state.output += escaped; - prev.value = escaped; - continue; - } - - // when the user specifies nothing, try to match both - prev.value = `(${capture}${escaped}|${prev.value})`; - state.output += prev.value; - continue; - } - - /** - * Braces - */ - - if (value === '{' && opts.nobrace !== true) { - increment('braces'); - - const open = { - type: 'brace', - value, - output: '(', - outputIndex: state.output.length, - tokensIndex: state.tokens.length - }; - - braces.push(open); - push(open); - continue; - } - - if (value === '}') { - const brace = braces[braces.length - 1]; - - if (opts.nobrace === true || !brace) { - push({ type: 'text', value, output: value }); - continue; - } - - let output = ')'; - - if (brace.dots === true) { - const arr = tokens.slice(); - const range = []; - - for (let i = arr.length - 1; i >= 0; i--) { - tokens.pop(); - if (arr[i].type === 'brace') { - break; - } - if (arr[i].type !== 'dots') { - range.unshift(arr[i].value); - } - } - - output = expandRange(range, opts); - state.backtrack = true; - } - - if (brace.comma !== true && brace.dots !== true) { - const out = state.output.slice(0, brace.outputIndex); - const toks = state.tokens.slice(brace.tokensIndex); - brace.value = brace.output = '\\{'; - value = output = '\\}'; - state.output = out; - for (const t of toks) { - state.output += (t.output || t.value); - } - } - - push({ type: 'brace', value, output }); - decrement('braces'); - braces.pop(); - continue; - } - - /** - * Pipes - */ - - if (value === '|') { - if (extglobs.length > 0) { - extglobs[extglobs.length - 1].conditions++; - } - push({ type: 'text', value }); - continue; - } - - /** - * Commas - */ - - if (value === ',') { - let output = value; - - const brace = braces[braces.length - 1]; - if (brace && stack[stack.length - 1] === 'braces') { - brace.comma = true; - output = '|'; - } - - push({ type: 'comma', value, output }); - continue; - } - - /** - * Slashes - */ - - if (value === '/') { - // if the beginning of the glob is "./", advance the start - // to the current index, and don't add the "./" characters - // to the state. This greatly simplifies lookbehinds when - // checking for BOS characters like "!" and "." (not "./") - if (prev.type === 'dot' && state.index === state.start + 1) { - state.start = state.index + 1; - state.consumed = ''; - state.output = ''; - tokens.pop(); - prev = bos; // reset "prev" to the first token - continue; - } - - push({ type: 'slash', value, output: SLASH_LITERAL }); - continue; - } - - /** - * Dots - */ - - if (value === '.') { - if (state.braces > 0 && prev.type === 'dot') { - if (prev.value === '.') prev.output = DOT_LITERAL; - const brace = braces[braces.length - 1]; - prev.type = 'dots'; - prev.output += value; - prev.value += value; - brace.dots = true; - continue; - } - - if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') { - push({ type: 'text', value, output: DOT_LITERAL }); - continue; - } - - push({ type: 'dot', value, output: DOT_LITERAL }); - continue; - } - - /** - * Question marks - */ - - if (value === '?') { - const isGroup = prev && prev.value === '('; - if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - extglobOpen('qmark', value); - continue; - } - - if (prev && prev.type === 'paren') { - const next = peek(); - let output = value; - - if (next === '<' && !utils.supportsLookbehinds()) { - throw new Error('Node.js v10 or higher is required for regex lookbehinds'); - } - - if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) { - output = `\\${value}`; - } - - push({ type: 'text', value, output }); - continue; - } - - if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) { - push({ type: 'qmark', value, output: QMARK_NO_DOT }); - continue; - } - - push({ type: 'qmark', value, output: QMARK }); - continue; - } - - /** - * Exclamation - */ - - if (value === '!') { - if (opts.noextglob !== true && peek() === '(') { - if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) { - extglobOpen('negate', value); - continue; - } - } - - if (opts.nonegate !== true && state.index === 0) { - negate(); - continue; - } - } - - /** - * Plus - */ - - if (value === '+') { - if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - extglobOpen('plus', value); - continue; - } - - if ((prev && prev.value === '(') || opts.regex === false) { - push({ type: 'plus', value, output: PLUS_LITERAL }); - continue; - } - - if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) { - push({ type: 'plus', value }); - continue; - } - - push({ type: 'plus', value: PLUS_LITERAL }); - continue; - } - - /** - * Plain text - */ - - if (value === '@') { - if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - push({ type: 'at', extglob: true, value, output: '' }); - continue; - } - - push({ type: 'text', value }); - continue; - } - - /** - * Plain text - */ - - if (value !== '*') { - if (value === '$' || value === '^') { - value = `\\${value}`; - } - - const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); - if (match) { - value += match[0]; - state.index += match[0].length; - } - - push({ type: 'text', value }); - continue; - } - - /** - * Stars - */ - - if (prev && (prev.type === 'globstar' || prev.star === true)) { - prev.type = 'star'; - prev.star = true; - prev.value += value; - prev.output = star; - state.backtrack = true; - state.globstar = true; - consume(value); - continue; - } - - let rest = remaining(); - if (opts.noextglob !== true && /^\([^?]/.test(rest)) { - extglobOpen('star', value); - continue; - } - - if (prev.type === 'star') { - if (opts.noglobstar === true) { - consume(value); - continue; - } - - const prior = prev.prev; - const before = prior.prev; - const isStart = prior.type === 'slash' || prior.type === 'bos'; - const afterStar = before && (before.type === 'star' || before.type === 'globstar'); - - if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) { - push({ type: 'star', value, output: '' }); - continue; - } - - const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); - const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren'); - if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) { - push({ type: 'star', value, output: '' }); - continue; - } - - // strip consecutive `/**/` - while (rest.slice(0, 3) === '/**') { - const after = input[state.index + 4]; - if (after && after !== '/') { - break; - } - rest = rest.slice(3); - consume('/**', 3); - } - - if (prior.type === 'bos' && eos()) { - prev.type = 'globstar'; - prev.value += value; - prev.output = globstar(opts); - state.output = prev.output; - state.globstar = true; - consume(value); - continue; - } - - if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) { - state.output = state.output.slice(0, -(prior.output + prev.output).length); - prior.output = `(?:${prior.output}`; - - prev.type = 'globstar'; - prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)'); - prev.value += value; - state.globstar = true; - state.output += prior.output + prev.output; - consume(value); - continue; - } - - if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') { - const end = rest[1] !== void 0 ? '|$' : ''; - - state.output = state.output.slice(0, -(prior.output + prev.output).length); - prior.output = `(?:${prior.output}`; - - prev.type = 'globstar'; - prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; - prev.value += value; - - state.output += prior.output + prev.output; - state.globstar = true; - - consume(value + advance()); - - push({ type: 'slash', value: '/', output: '' }); - continue; - } - - if (prior.type === 'bos' && rest[0] === '/') { - prev.type = 'globstar'; - prev.value += value; - prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; - state.output = prev.output; - state.globstar = true; - consume(value + advance()); - push({ type: 'slash', value: '/', output: '' }); - continue; - } - - // remove single star from output - state.output = state.output.slice(0, -prev.output.length); - - // reset previous token to globstar - prev.type = 'globstar'; - prev.output = globstar(opts); - prev.value += value; - - // reset output with globstar - state.output += prev.output; - state.globstar = true; - consume(value); - continue; - } - - const token = { type: 'star', value, output: star }; - - if (opts.bash === true) { - token.output = '.*?'; - if (prev.type === 'bos' || prev.type === 'slash') { - token.output = nodot + token.output; - } - push(token); - continue; - } - - if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) { - token.output = value; - push(token); - continue; - } - - if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') { - if (prev.type === 'dot') { - state.output += NO_DOT_SLASH; - prev.output += NO_DOT_SLASH; - - } else if (opts.dot === true) { - state.output += NO_DOTS_SLASH; - prev.output += NO_DOTS_SLASH; - - } else { - state.output += nodot; - prev.output += nodot; - } - - if (peek() !== '*') { - state.output += ONE_CHAR; - prev.output += ONE_CHAR; - } - } - - push(token); - } - - while (state.brackets > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']')); - state.output = utils.escapeLast(state.output, '['); - decrement('brackets'); - } - - while (state.parens > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')')); - state.output = utils.escapeLast(state.output, '('); - decrement('parens'); - } - - while (state.braces > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}')); - state.output = utils.escapeLast(state.output, '{'); - decrement('braces'); - } - - if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) { - push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` }); - } - - // rebuild the output if we had to backtrack at any point - if (state.backtrack === true) { - state.output = ''; - - for (const token of state.tokens) { - state.output += token.output != null ? token.output : token.value; - - if (token.suffix) { - state.output += token.suffix; - } - } - } - - return state; -}; - -/** - * Fast paths for creating regular expressions for common glob patterns. - * This can significantly speed up processing and has very little downside - * impact when none of the fast paths match. - */ - -parse.fastpaths = (input, options) => { - const opts = { ...options }; - const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; - const len = input.length; - if (len > max) { - throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); - } - - input = REPLACEMENTS[input] || input; - const win32 = utils.isWindows(options); - - // create constants based on platform, for windows or posix - const { - DOT_LITERAL, - SLASH_LITERAL, - ONE_CHAR, - DOTS_SLASH, - NO_DOT, - NO_DOTS, - NO_DOTS_SLASH, - STAR, - START_ANCHOR - } = constants.globChars(win32); - - const nodot = opts.dot ? NO_DOTS : NO_DOT; - const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; - const capture = opts.capture ? '' : '?:'; - const state = { negated: false, prefix: '' }; - let star = opts.bash === true ? '.*?' : STAR; - - if (opts.capture) { - star = `(${star})`; - } - - const globstar = opts => { - if (opts.noglobstar === true) return star; - return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; - }; - - const create = str => { - switch (str) { - case '*': - return `${nodot}${ONE_CHAR}${star}`; - - case '.*': - return `${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '*.*': - return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '*/*': - return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; - - case '**': - return nodot + globstar(opts); - - case '**/*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; - - case '**/*.*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '**/.*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; - - default: { - const match = /^(.*?)\.(\w+)$/.exec(str); - if (!match) return; - - const source = create(match[1]); - if (!source) return; - - return source + DOT_LITERAL + match[2]; - } - } - }; - - const output = utils.removePrefix(input, state); - let source = create(output); - - if (source && opts.strictSlashes !== true) { - source += `${SLASH_LITERAL}?`; - } - - return source; -}; - -module.exports = parse; - - -/***/ }), -/* 793 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.merge = void 0; -const merge2 = __webpack_require__(243); -function merge(streams) { - const mergedStream = merge2(streams); - streams.forEach((stream) => { - stream.once('error', (error) => mergedStream.emit('error', error)); - }); - mergedStream.once('close', () => propagateCloseEventToSources(streams)); - mergedStream.once('end', () => propagateCloseEventToSources(streams)); - return mergedStream; -} -exports.merge = merge; -function propagateCloseEventToSources(streams) { - streams.forEach((stream) => stream.emit('close')); -} - - -/***/ }), -/* 794 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEmpty = exports.isString = void 0; -function isString(input) { - return typeof input === 'string'; -} -exports.isString = isString; -function isEmpty(input) { - return input === ''; -} -exports.isEmpty = isEmpty; - - -/***/ }), -/* 795 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(796); -const provider_1 = __webpack_require__(798); -class ProviderAsync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = []; - return new Promise((resolve, reject) => { - const stream = this.api(root, task, options); - stream.once('error', reject); - stream.on('data', (entry) => entries.push(options.transform(entry))); - stream.once('end', () => resolve(entries)); - }); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderAsync; - - -/***/ }), -/* 796 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(173); -const fsStat = __webpack_require__(289); -const fsWalk = __webpack_require__(294); -const reader_1 = __webpack_require__(797); -class ReaderStream extends reader_1.default { - constructor() { - super(...arguments); - this._walkStream = fsWalk.walkStream; - this._stat = fsStat.stat; - } - dynamic(root, options) { - return this._walkStream(root, options); - } - static(patterns, options) { - const filepaths = patterns.map(this._getFullEntryPath, this); - const stream = new stream_1.PassThrough({ objectMode: true }); - stream._write = (index, _enc, done) => { - return this._getEntry(filepaths[index], patterns[index], options) - .then((entry) => { - if (entry !== null && options.entryFilter(entry)) { - stream.push(entry); - } - if (index === filepaths.length - 1) { - stream.end(); - } - done(); - }) - .catch(done); - }; - for (let i = 0; i < filepaths.length; i++) { - stream.write(i); - } - return stream; - } - _getEntry(filepath, pattern, options) { - return this._getStat(filepath) - .then((stats) => this._makeEntry(stats, pattern)) - .catch((error) => { - if (options.errorFilter(error)) { - return null; - } - throw error; - }); - } - _getStat(filepath) { - return new Promise((resolve, reject) => { - this._stat(filepath, this._fsStatSettings, (error, stats) => { - return error === null ? resolve(stats) : reject(error); - }); - }); - } -} -exports.default = ReaderStream; - - -/***/ }), -/* 797 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsStat = __webpack_require__(289); -const utils = __webpack_require__(780); -class Reader { - constructor(_settings) { - this._settings = _settings; - this._fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this._settings.followSymbolicLinks, - fs: this._settings.fs, - throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks - }); - } - _getFullEntryPath(filepath) { - return path.resolve(this._settings.cwd, filepath); - } - _makeEntry(stats, pattern) { - const entry = { - name: pattern, - path: pattern, - dirent: utils.fs.createDirentFromStats(pattern, stats) - }; - if (this._settings.stats) { - entry.stats = stats; - } - return entry; - } - _isFatalError(error) { - return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; - } -} -exports.default = Reader; - - -/***/ }), -/* 798 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const deep_1 = __webpack_require__(799); -const entry_1 = __webpack_require__(802); -const error_1 = __webpack_require__(803); -const entry_2 = __webpack_require__(804); -class Provider { - constructor(_settings) { - this._settings = _settings; - this.errorFilter = new error_1.default(this._settings); - this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); - this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); - this.entryTransformer = new entry_2.default(this._settings); - } - _getRootDirectory(task) { - return path.resolve(this._settings.cwd, task.base); - } - _getReaderOptions(task) { - const basePath = task.base === '.' ? '' : task.base; - return { - basePath, - pathSegmentSeparator: '/', - concurrency: this._settings.concurrency, - deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), - entryFilter: this.entryFilter.getFilter(task.positive, task.negative), - errorFilter: this.errorFilter.getFilter(), - followSymbolicLinks: this._settings.followSymbolicLinks, - fs: this._settings.fs, - stats: this._settings.stats, - throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, - transform: this.entryTransformer.getTransformer() - }; - } - _getMicromatchOptions() { - return { - dot: this._settings.dot, - matchBase: this._settings.baseNameMatch, - nobrace: !this._settings.braceExpansion, - nocase: !this._settings.caseSensitiveMatch, - noext: !this._settings.extglob, - noglobstar: !this._settings.globstar, - posix: true, - strictSlashes: false - }; - } -} -exports.default = Provider; - - -/***/ }), -/* 799 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(780); -const partial_1 = __webpack_require__(800); -class DeepFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - } - getFilter(basePath, positive, negative) { - const matcher = this._getMatcher(positive); - const negativeRe = this._getNegativePatternsRe(negative); - return (entry) => this._filter(basePath, entry, matcher, negativeRe); - } - _getMatcher(patterns) { - return new partial_1.default(patterns, this._settings, this._micromatchOptions); - } - _getNegativePatternsRe(patterns) { - const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); - return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); - } - _filter(basePath, entry, matcher, negativeRe) { - if (this._isSkippedByDeep(basePath, entry.path)) { - return false; - } - if (this._isSkippedSymbolicLink(entry)) { - return false; - } - const filepath = utils.path.removeLeadingDotSegment(entry.path); - if (this._isSkippedByPositivePatterns(filepath, matcher)) { - return false; - } - return this._isSkippedByNegativePatterns(filepath, negativeRe); - } - _isSkippedByDeep(basePath, entryPath) { - /** - * Avoid unnecessary depth calculations when it doesn't matter. - */ - if (this._settings.deep === Infinity) { - return false; - } - return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; - } - _getEntryLevel(basePath, entryPath) { - const entryPathDepth = entryPath.split('/').length; - if (basePath === '') { - return entryPathDepth; - } - const basePathDepth = basePath.split('/').length; - return entryPathDepth - basePathDepth; - } - _isSkippedSymbolicLink(entry) { - return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); - } - _isSkippedByPositivePatterns(entryPath, matcher) { - return !this._settings.baseNameMatch && !matcher.match(entryPath); - } - _isSkippedByNegativePatterns(entryPath, patternsRe) { - return !utils.pattern.matchAny(entryPath, patternsRe); - } -} -exports.default = DeepFilter; - - -/***/ }), -/* 800 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(801); -class PartialMatcher extends matcher_1.default { - match(filepath) { - const parts = filepath.split('/'); - const levels = parts.length; - const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); - for (const pattern of patterns) { - const section = pattern.sections[0]; - /** - * In this case, the pattern has a globstar and we must read all directories unconditionally, - * but only if the level has reached the end of the first group. - * - * fixtures/{a,b}/** - * ^ true/false ^ always true - */ - if (!pattern.complete && levels > section.length) { - return true; - } - const match = parts.every((part, index) => { - const segment = pattern.segments[index]; - if (segment.dynamic && segment.patternRe.test(part)) { - return true; - } - if (!segment.dynamic && segment.pattern === part) { - return true; - } - return false; - }); - if (match) { - return true; - } - } - return false; - } -} -exports.default = PartialMatcher; - - -/***/ }), -/* 801 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; +"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const utils = __webpack_require__(780); @@ -92520,7 +90415,7 @@ exports.default = Matcher; /***/ }), -/* 802 */ +/* 796 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92583,7 +90478,7 @@ exports.default = EntryFilter; /***/ }), -/* 803 */ +/* 797 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92605,7 +90500,7 @@ exports.default = ErrorFilter; /***/ }), -/* 804 */ +/* 798 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92638,15 +90533,15 @@ exports.default = EntryTransformer; /***/ }), -/* 805 */ +/* 799 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(173); -const stream_2 = __webpack_require__(796); -const provider_1 = __webpack_require__(798); +const stream_2 = __webpack_require__(790); +const provider_1 = __webpack_require__(792); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -92676,14 +90571,14 @@ exports.default = ProviderStream; /***/ }), -/* 806 */ +/* 800 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(807); -const provider_1 = __webpack_require__(798); +const sync_1 = __webpack_require__(801); +const provider_1 = __webpack_require__(792); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -92706,7 +90601,7 @@ exports.default = ProviderSync; /***/ }), -/* 807 */ +/* 801 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92714,7 +90609,7 @@ exports.default = ProviderSync; Object.defineProperty(exports, "__esModule", { value: true }); const fsStat = __webpack_require__(289); const fsWalk = __webpack_require__(294); -const reader_1 = __webpack_require__(797); +const reader_1 = __webpack_require__(791); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -92756,7 +90651,7 @@ exports.default = ReaderSync; /***/ }), -/* 808 */ +/* 802 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92820,7 +90715,7 @@ exports.default = Settings; /***/ }), -/* 809 */ +/* 803 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92947,7 +90842,7 @@ module.exports.sync = options => { /***/ }), -/* 810 */ +/* 804 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -93000,7 +90895,7 @@ module.exports = { /***/ }), -/* 811 */ +/* 805 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/yarn.lock b/yarn.lock index 6867f4edc8191..2bc338e6bad46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.0.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.1.tgz#7922fb0817bf3166d8d9e258c57477e3fd1c3610" - integrity sha512-Aolwjd7HSC2PyY0fDj/wA/EimQT4HfEnFYNp5s9CQlrdhyvWTtvZ5YzrUPu6R6/1jKiUlxu8bUhkdSnKHNAHMA== +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== dependencies: "@jridgewell/trace-mapping" "^0.3.0" @@ -41,11 +41,12 @@ call-me-maybe "^1.0.1" z-schema "^5.0.1" -"@babel/cli@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.17.0.tgz#9b932d8f08a2e218fcdd9bba456044eb0a2e0b2c" - integrity sha512-es10YH/ejXbg551vtnmEzIPe3MQRNOS644o3pf8vUr1tIeNzVNlP8BBvs1Eh7roh5A+k2fEHUas+ZptOWHA1fQ== +"@babel/cli@^7.17.6": + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.17.6.tgz#169e5935f1795f0b62ded5a2accafeedfe5c5363" + integrity sha512-l4w608nsDNlxZhiJ5tE3DbNmr61fIKMZ6fTBo171VEFuFMIYuJ3mHRhTLEkKKyvx2Mizkkv/0a8OJOnZqkKYNA== dependencies: + "@jridgewell/trace-mapping" "^0.3.4" commander "^4.0.1" convert-source-map "^1.1.0" fs-readdir-recursive "^1.1.0" @@ -71,34 +72,17 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" - integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== - dependencies: - "@babel/highlight" "^7.16.0" - -"@babel/code-frame@^7.16.7": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.0.tgz#ea269d7f78deb3a7826c39a4048eecda541ebdaa" - integrity sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew== - -"@babel/compat-data@^7.16.4": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" - integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== - -"@babel/compat-data@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60" - integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34" + integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng== "@babel/core@7.12.9": version "7.12.9" @@ -122,41 +106,20 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.5": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.0.tgz#c4ff44046f5fe310525cc9eb4ef5147f0c5374d4" - integrity sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.0" - "@babel/helper-compilation-targets" "^7.16.0" - "@babel/helper-module-transforms" "^7.16.0" - "@babel/helpers" "^7.16.0" - "@babel/parser" "^7.16.0" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.0" - "@babel/types" "^7.16.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/core@^7.17.2": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.2.tgz#2c77fc430e95139d816d39b113b31bf40fb22337" - integrity sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw== +"@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.17.5", "@babel/core@^7.7.5": + version "7.17.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" + integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA== dependencies: - "@ampproject/remapping" "^2.0.0" + "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.0" + "@babel/generator" "^7.17.3" "@babel/helper-compilation-targets" "^7.16.7" "@babel/helper-module-transforms" "^7.16.7" "@babel/helpers" "^7.17.2" - "@babel/parser" "^7.17.0" + "@babel/parser" "^7.17.3" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.0" + "@babel/traverse" "^7.17.3" "@babel/types" "^7.17.0" convert-source-map "^1.7.0" debug "^4.1.0" @@ -180,55 +143,22 @@ dependencies: eslint-rule-composer "^0.3.0" -"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" - integrity sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew== - dependencies: - "@babel/types" "^7.16.0" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" - integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== - dependencies: - "@babel/types" "^7.16.8" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.0.tgz#7bd890ba706cd86d3e2f727322346ffdbf98f65e" - integrity sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw== +"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200" + integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg== dependencies: "@babel/types" "^7.17.0" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz#9a1f0ebcda53d9a2d00108c4ceace6a5d5f1f08d" - integrity sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-annotate-as-pure@^7.16.7": +"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== dependencies: "@babel/types" "^7.16.7" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.0.tgz#f1a686b92da794020c26582eb852e9accd0d7882" - integrity sha512-9KuleLT0e77wFUku6TUkqZzCEymBdtuQQ27MhEKzf9UOOJu3cYj98kyaDAzxpC7lV6DGiZFuC8XqDsq8/Kl6aQ== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.16.0" - "@babel/types" "^7.16.0" - "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" @@ -237,17 +167,7 @@ "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.0", "@babel/helper-compilation-targets@^7.16.3": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz#5b480cd13f68363df6ec4dc8ac8e2da11363cbf0" - integrity sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA== - dependencies: - "@babel/compat-data" "^7.16.0" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.17.5" - semver "^6.3.0" - -"@babel/helper-compilation-targets@^7.16.7": +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== @@ -257,19 +177,7 @@ browserslist "^4.17.5" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz#090d4d166b342a03a9fec37ef4fd5aeb9c7c6a4b" - integrity sha512-XLwWvqEaq19zFlF5PTgOod4bUA+XbkR4WLQBct1bkzmxJGB0ZEJaoKF4c8cgH9oBtCDuYJ8BP5NB9uFiEgO5QA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-member-expression-to-functions" "^7.16.0" - "@babel/helper-optimise-call-expression" "^7.16.0" - "@babel/helper-replace-supers" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - -"@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7": +"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7": version "7.16.10" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz#8a6959b9cc818a88815ba3c5474619e9c0f2c21c" integrity sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg== @@ -282,14 +190,6 @@ "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" -"@babel/helper-create-regexp-features-plugin@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz#06b2348ce37fccc4f5e18dcd8d75053f2a7c44ff" - integrity sha512-3DyG0zAFAZKcOp7aVr33ddwkxJ0Z0Jr5V99y3I690eYLpukJsJvAbzTy1ewoCqsML8SbIrjH14Jc/nSQ4TvNPA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - regexpu-core "^4.7.1" - "@babel/helper-create-regexp-features-plugin@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48" @@ -312,21 +212,7 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-define-polyfill-provider@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.0.tgz#c5b10cf4b324ff840140bb07e05b8564af2ae971" - integrity sha512-7hfT8lUljl/tM3h+izTX/pO3W3frz2ok6Pk+gzys8iJqDfZrZy2pXjRTZAvG2YmfHun1X4q8/UZRLatMfqc5Tg== - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-define-polyfill-provider@^0.3.1": +"@babel/helper-define-polyfill-provider@^0.3.0", "@babel/helper-define-polyfill-provider@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== @@ -347,13 +233,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-explode-assignable-expression@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz#753017337a15f46f9c09f674cff10cee9b9d7778" - integrity sha512-Hk2SLxC9ZbcOhLpg/yMznzJ11W++lg5GMbxt1ev6TXUiJB0N42KPC+7w8a+eWGuqDnUYuwStJoZHM7RgmIOaGQ== - dependencies: - "@babel/types" "^7.16.0" - "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" @@ -361,15 +240,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" - integrity sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog== - dependencies: - "@babel/helper-get-function-arity" "^7.16.0" - "@babel/template" "^7.16.0" - "@babel/types" "^7.16.0" - "@babel/helper-function-name@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" @@ -379,13 +249,6 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-get-function-arity@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" - integrity sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ== - dependencies: - "@babel/types" "^7.16.0" - "@babel/helper-get-function-arity@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" @@ -393,13 +256,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" - integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg== - dependencies: - "@babel/types" "^7.16.0" - "@babel/helper-hoist-variables@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" @@ -407,13 +263,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-member-expression-to-functions@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz#29287040efd197c77636ef75188e81da8bccd5a4" - integrity sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ== - dependencies: - "@babel/types" "^7.16.0" - "@babel/helper-member-expression-to-functions@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" @@ -421,35 +270,14 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.7.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz#90538e60b672ecf1b448f5f4f5433d37e79a3ec3" - integrity sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-module-imports@^7.16.7": +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.7.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5" - integrity sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA== - dependencies: - "@babel/helper-module-imports" "^7.16.0" - "@babel/helper-replace-supers" "^7.16.0" - "@babel/helper-simple-access" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.0" - "@babel/types" "^7.16.0" - -"@babel/helper-module-transforms@^7.16.7": +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== @@ -463,13 +291,6 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-optimise-call-expression@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz#cecdb145d70c54096b1564f8e9f10cd7d193b338" - integrity sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw== - dependencies: - "@babel/types" "^7.16.0" - "@babel/helper-optimise-call-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" @@ -482,34 +303,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-plugin-utils@^7.16.7": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== -"@babel/helper-remap-async-to-generator@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.0.tgz#d5aa3b086e13a5fe05238ff40c3a5a0c2dab3ead" - integrity sha512-MLM1IOMe9aQBqMWxcRw8dcb9jlM86NIw7KA0Wri91Xkfied+dE0QuBFSBjMNvqzmS0OSIDsMNC24dBEkPUi7ew== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-wrap-function" "^7.16.0" - "@babel/types" "^7.16.0" - -"@babel/helper-remap-async-to-generator@^7.16.4": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.4.tgz#5d7902f61349ff6b963e07f06a389ce139fbfe6e" - integrity sha512-vGERmmhR+s7eH5Y/cp8PCVzj4XEjerq8jooMfxFdA5xVtAk9Sh4AQsrWgiErUEBjtGrBtOFKDUcWQFW4/dFwMA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-wrap-function" "^7.16.0" - "@babel/types" "^7.16.0" - "@babel/helper-remap-async-to-generator@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" @@ -519,16 +317,6 @@ "@babel/helper-wrap-function" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helper-replace-supers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz#73055e8d3cf9bcba8ddb55cad93fedc860f68f17" - integrity sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.16.0" - "@babel/helper-optimise-call-expression" "^7.16.0" - "@babel/traverse" "^7.16.0" - "@babel/types" "^7.16.0" - "@babel/helper-replace-supers@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" @@ -540,13 +328,6 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz#21d6a27620e383e37534cf6c10bba019a6f90517" - integrity sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw== - dependencies: - "@babel/types" "^7.16.0" - "@babel/helper-simple-access@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" @@ -561,13 +342,6 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-split-export-declaration@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" - integrity sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw== - dependencies: - "@babel/types" "^7.16.0" - "@babel/helper-split-export-declaration@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" @@ -575,36 +349,16 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-validator-identifier@^7.15.7": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - "@babel/helper-validator-identifier@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== - "@babel/helper-validator-option@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== -"@babel/helper-wrap-function@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.0.tgz#b3cf318afce774dfe75b86767cd6d68f3482e57c" - integrity sha512-VVMGzYY3vkWgCJML+qVLvGIam902mJW0FvT7Avj1zEe0Gn7D93aWdLblYARTxEw+6DhZmtzhBM2zv0ekE5zg1g== - dependencies: - "@babel/helper-function-name" "^7.16.0" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.0" - "@babel/types" "^7.16.0" - "@babel/helper-wrap-function@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" @@ -615,16 +369,7 @@ "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helpers@^7.12.5", "@babel/helpers@^7.16.0": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.3.tgz#27fc64f40b996e7074dc73128c3e5c3e7f55c43c" - integrity sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w== - dependencies: - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.3" - "@babel/types" "^7.16.0" - -"@babel/helpers@^7.17.2": +"@babel/helpers@^7.12.5", "@babel/helpers@^7.17.2": version "7.17.2" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== @@ -633,16 +378,7 @@ "@babel/traverse" "^7.17.0" "@babel/types" "^7.17.0" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" - integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.16.7": +"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": version "7.16.10" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== @@ -651,27 +387,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.16.0", "@babel/parser@^7.16.3": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" - integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== - -"@babel/parser@^7.16.10", "@babel/parser@^7.16.7": - version "7.16.12" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.12.tgz#9474794f9a650cf5e2f892444227f98e28cdf8b6" - integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A== - -"@babel/parser@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.0.tgz#f0ac33eddbe214e4105363bb17c3341c5ffcc43c" - integrity sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw== - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.2": - version "7.16.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz#2977fca9b212db153c195674e57cfab807733183" - integrity sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" +"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0" + integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": version "7.16.7" @@ -680,15 +399,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.0.tgz#358972eaab006f5eb0826183b0c93cbcaf13e1e2" - integrity sha512-4tcFwwicpWTrpl9qjf7UsoosaArgImF85AxqCRZlgc3IQDvkUHjJpruXAL58Wmj+T6fypWTC/BakfEkwIL/pwA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.16.0" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" @@ -698,15 +408,6 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.7" -"@babel/plugin-proposal-async-generator-functions@^7.16.4": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.4.tgz#e606eb6015fec6fa5978c940f315eae4e300b081" - integrity sha512-/CUekqaAaZCQHleSK/9HajvcD/zdnJiKRiuUFq8ITE+0HsPzquf53cpFiqAwl/UfmJbR6n5uGPQSPdrmKOvHHg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.16.4" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-proposal-async-generator-functions@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" @@ -716,15 +417,7 @@ "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz#c029618267ddebc7280fa286e0f8ca2a278a2d1a" - integrity sha512-mCF3HcuZSY9Fcx56Lbn+CGdT44ioBMMvjNVldpKtj8tpniETdLjnxdHI1+sDWXIM1nNt+EanJOZ3IG9lzVjs7A== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-class-properties@^7.16.7": +"@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== @@ -732,15 +425,6 @@ "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-proposal-class-static-block@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.0.tgz#5296942c564d8144c83eea347d0aa8a0b89170e7" - integrity sha512-mAy3sdcY9sKAkf3lQbDiv3olOfiLqI51c9DR9b19uMoR2Z6r5pmGl7dfNFqEvqOyqbf1ta4lknK4gc5PJn3mfA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-proposal-class-static-block@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz#712357570b612106ef5426d13dc433ce0f200c2a" @@ -759,14 +443,6 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-decorators" "^7.12.13" -"@babel/plugin-proposal-dynamic-import@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.0.tgz#783eca61d50526202f9b296095453977e88659f1" - integrity sha512-QGSA6ExWk95jFQgwz5GQ2Dr95cf7eI7TKutIXXTb7B1gCLTCz5hTjFTQGfLFBBiC5WSNi7udNwWsqbbMh1c4yQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-dynamic-import@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" @@ -783,14 +459,6 @@ "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-export-default-from" "^7.12.13" -"@babel/plugin-proposal-export-namespace-from@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.0.tgz#9c01dee40b9d6b847b656aaf4a3976a71740f222" - integrity sha512-CjI4nxM/D+5wCnhD11MHB1AwRSAYeDT+h8gCdcVJZ/OK7+wRzFsf7PFPWVpVpNRkHMmMkQWAHpTq+15IXQ1diA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-proposal-export-namespace-from@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" @@ -799,14 +467,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.0.tgz#cae35a95ed1d2a7fa29c4dc41540b84a72e9ab25" - integrity sha512-kouIPuiv8mSi5JkEhzApg5Gn6hFyKPnlkO0a9YSzqRurH8wYzSlf6RJdzluAsbqecdW5pBvDJDfyDIUR/vLxvg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-proposal-json-strings@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" @@ -815,14 +475,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-logical-assignment-operators@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.0.tgz#a711b8ceb3ffddd3ef88d3a49e86dbd3cc7db3fd" - integrity sha512-pbW0fE30sVTYXXm9lpVQQ/Vc+iTeQKiXlaNRZPPN2A2VdlWyAtsUrsQ3xydSlDW00TFMK7a8m3cDTkBF5WnV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-proposal-logical-assignment-operators@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" @@ -831,15 +483,7 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.0.tgz#44e1cce08fe2427482cf446a91bb451528ed0596" - integrity sha512-3bnHA8CAFm7cG93v8loghDYyQ8r97Qydf63BeYiGgYbjKKB/XP53W15wfRC7dvKfoiJ34f6Rbyyx2btExc8XsQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== @@ -847,14 +491,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-numeric-separator@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.0.tgz#5d418e4fbbf8b9b7d03125d3a52730433a373734" - integrity sha512-FAhE2I6mjispy+vwwd6xWPyEx3NYFS13pikDBWUAFGZvq6POGs5eNchw8+1CYoEgBl9n11I3NkzD7ghn25PQ9Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-proposal-numeric-separator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" @@ -872,36 +508,17 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-transform-parameters" "^7.12.1" -"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.0.tgz#5fb32f6d924d6e6712810362a60e12a2609872e6" - integrity sha512-LU/+jp89efe5HuWJLmMmFG0+xbz+I2rSI7iLc1AlaeSMDMOGzWlc5yJrMN1d04osXN4sSfpo4O+azkBNBes0jg== - dependencies: - "@babel/compat-data" "^7.16.0" - "@babel/helper-compilation-targets" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.16.0" - -"@babel/plugin-proposal-object-rest-spread@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz#94593ef1ddf37021a25bdcb5754c4a8d534b01d8" - integrity sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA== +"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.16.0", "@babel/plugin-proposal-object-rest-spread@^7.16.7", "@babel/plugin-proposal-object-rest-spread@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" + integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== dependencies: - "@babel/compat-data" "^7.16.4" + "@babel/compat-data" "^7.17.0" "@babel/helper-compilation-targets" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.16.7" -"@babel/plugin-proposal-optional-catch-binding@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.0.tgz#5910085811ab4c28b00d6ebffa4ab0274d1e5f16" - integrity sha512-kicDo0A/5J0nrsCPbn89mTG3Bm4XgYi0CZtvex9Oyw7gGZE3HXGD0zpQNH+mo+tEfbo8wbmMvJftOwpmPy7aVw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-catch-binding@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" @@ -910,16 +527,7 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.0.tgz#56dbc3970825683608e9efb55ea82c2a2d6c8dc0" - integrity sha512-Y4rFpkZODfHrVo70Uaj6cC1JJOt3Pp0MdWSwIKtb8z1/lsjl9AmnB7ErRFV+QNGIfcY1Eruc2UMx5KaRnXjMyg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.16.7": +"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== @@ -928,15 +536,7 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-private-methods@^7.12.1", "@babel/plugin-proposal-private-methods@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.0.tgz#b4dafb9c717e4301c5776b30d080d6383c89aff6" - integrity sha512-IvHmcTHDFztQGnn6aWq4t12QaBXTKr1whF/dgp9kz84X6GUcwq9utj7z2wFCUfeOup/QKnOlt2k0zxkGFx9ubg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-private-methods@^7.16.11": +"@babel/plugin-proposal-private-methods@^7.12.1", "@babel/plugin-proposal-private-methods@^7.16.11": version "7.16.11" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw== @@ -944,16 +544,6 @@ "@babel/helper-create-class-features-plugin" "^7.16.10" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-proposal-private-property-in-object@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.0.tgz#69e935b2c5c79d2488112d886f0c4e2790fee76f" - integrity sha512-3jQUr/HBbMVZmi72LpjQwlZ55i1queL8KcDTQEkAHihttJnAPrcvG9ZNXIfsd2ugpizZo595egYV6xy+pv4Ofw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-create-class-features-plugin" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-proposal-private-property-in-object@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" @@ -964,15 +554,7 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.16.0", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.0.tgz#890482dfc5ea378e42e19a71e709728cabf18612" - integrity sha512-ti7IdM54NXv29cA4+bNNKEMS4jLMCbJgl+Drv+FgYy0erJLAxNAIXcNjNjrRZEcWq0xJHsNVwQezskMFpF8N9g== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.16.7": +"@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== @@ -1064,14 +646,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-jsx@^7.12.1", "@babel/plugin-syntax-jsx@^7.16.0", "@babel/plugin-syntax-jsx@^7.2.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.0.tgz#f9624394317365a9a88c82358d3f8471154698f1" - integrity sha512-8zv2+xiPHwly31RK4RmnEYY5zziuF3O7W2kIDW+07ewWDh6Oi0dRq8kwvulRkFgt6DB97RlKs5c1y068iPlCUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-jsx@^7.16.7": +"@babel/plugin-syntax-jsx@^7.12.1", "@babel/plugin-syntax-jsx@^7.16.7", "@babel/plugin-syntax-jsx@^7.2.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== @@ -1134,13 +709,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.0.tgz#2feeb13d9334cc582ea9111d3506f773174179bb" - integrity sha512-Xv6mEXqVdaqCBfJFyeab0fH2DnUoMsDmhamxsSi4j8nLd4Vtw213WMJr55xxqipC/YVWyPY3K0blJncPYji+dQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" @@ -1148,29 +716,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.0.tgz#951706f8b449c834ed07bd474c0924c944b95a8e" - integrity sha512-vIFb5250Rbh7roWARvCLvIJ/PtAU5Lhv7BtZ1u24COwpI9Ypjsh+bZcKk6rlIyalK+r0jOc1XQ8I4ovNxNrWrA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-arrow-functions@^7.16.7": +"@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-async-to-generator@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.0.tgz#df12637f9630ddfa0ef9d7a11bc414d629d38604" - integrity sha512-PbIr7G9kR8tdH6g8Wouir5uVjklETk91GMVSUq+VaOgiinbCkBP6Q7NN/suM/QutZkMJMvcyAriogcYAdhg8Gw== - dependencies: - "@babel/helper-module-imports" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.16.0" - "@babel/plugin-transform-async-to-generator@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" @@ -1180,13 +732,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-remap-async-to-generator" "^7.16.8" -"@babel/plugin-transform-block-scoped-functions@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.0.tgz#c618763233ad02847805abcac4c345ce9de7145d" - integrity sha512-V14As3haUOP4ZWrLJ3VVx5rCnrYhMSHN/jX7z6FAt5hjRkLsb0snPCmJwSOML5oxkKO4FNoNv7V5hw/y2bjuvg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-block-scoped-functions@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" @@ -1194,34 +739,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-block-scoping@^7.12.12", "@babel/plugin-transform-block-scoping@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.0.tgz#bcf433fb482fe8c3d3b4e8a66b1c4a8e77d37c16" - integrity sha512-27n3l67/R3UrXfizlvHGuTwsRIFyce3D/6a37GRxn28iyTPvNXaW4XvznexRh1zUNLPjbLL22Id0XQElV94ruw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-block-scoping@^7.16.7": +"@babel/plugin-transform-block-scoping@^7.12.12", "@babel/plugin-transform-block-scoping@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.0.tgz#54cf5ff0b2242c6573d753cd4bfc7077a8b282f5" - integrity sha512-HUxMvy6GtAdd+GKBNYDWCIA776byUQH8zjnfjxwT1P1ARv/wFu8eBDpmXQcLS/IwRtrxIReGiplOwMeyO7nsDQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-optimise-call-expression" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - globals "^11.1.0" - -"@babel/plugin-transform-classes@^7.16.7": +"@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== @@ -1235,13 +760,6 @@ "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.0.tgz#e0c385507d21e1b0b076d66bed6d5231b85110b7" - integrity sha512-63l1dRXday6S8V3WFY5mXJwcRAnPYxvFfTlt67bwV1rTyVTM5zrp0DBBb13Kl7+ehkCVwIZPumPpFP/4u70+Tw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-computed-properties@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" @@ -1249,29 +767,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.0.tgz#ad3d7e74584ad5ea4eadb1e6642146c590dee33c" - integrity sha512-Q7tBUwjxLTsHEoqktemHBMtb3NYwyJPTJdM+wDwb0g8PZ3kQUIzNvwD5lPaqW/p54TXBc/MXZu9Jr7tbUEUM8Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-destructuring@^7.16.7": +"@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz#ca9588ae2d63978a4c29d3f33282d8603f618e23" integrity sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A== dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-dotall-regex@^7.16.0", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.0.tgz#50bab00c1084b6162d0a58a818031cf57798e06f" - integrity sha512-FXlDZfQeLILfJlC6I1qyEwcHK5UpRCFkaoVyA1nk9A1L1Yu583YO4un2KsLBsu3IJb4CUbctZks8tD9xPQubLw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-dotall-regex@^7.16.7": +"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== @@ -1279,13 +782,6 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-duplicate-keys@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.0.tgz#8bc2e21813e3e89e5e5bf3b60aa5fc458575a176" - integrity sha512-LIe2kcHKAZOJDNxujvmp6z3mfN6V9lJxubU4fJIGoQCkKe3Ec2OcbdlYP+vW++4MpxwG0d1wSDOJtQW5kLnkZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-duplicate-keys@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" @@ -1293,14 +789,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-exponentiation-operator@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.0.tgz#a180cd2881e3533cef9d3901e48dad0fbeff4be4" - integrity sha512-OwYEvzFI38hXklsrbNivzpO3fh87skzx8Pnqi4LoSYeav0xHlueSoCJrSgTPfnbyzopo5b3YVAJkFIcUpK2wsw== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-exponentiation-operator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" @@ -1317,28 +805,13 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-flow" "^7.12.13" -"@babel/plugin-transform-for-of@^7.12.1", "@babel/plugin-transform-for-of@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.0.tgz#f7abaced155260e2461359bbc7c7248aca5e6bd2" - integrity sha512-5QKUw2kO+GVmKr2wMYSATCTTnHyscl6sxFRAY+rvN7h7WB0lcG0o4NoV6ZQU32OZGVsYUsfLGgPQpDFdkfjlJQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-for-of@^7.16.7": +"@babel/plugin-transform-for-of@^7.12.1", "@babel/plugin-transform-for-of@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-function-name@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.0.tgz#02e3699c284c6262236599f751065c5d5f1f400e" - integrity sha512-lBzMle9jcOXtSOXUpc7tvvTpENu/NuekNJVova5lCCWCV9/U1ho2HH2y0p6mBg8fPm/syEAbfaaemYGOHCY3mg== - dependencies: - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-function-name@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" @@ -1348,13 +821,6 @@ "@babel/helper-function-name" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-literals@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.0.tgz#79711e670ffceb31bd298229d50f3621f7980cac" - integrity sha512-gQDlsSF1iv9RU04clgXqRjrPyyoJMTclFt3K1cjLmTKikc0s/6vE3hlDeEVC71wLTRu72Fq7650kABrdTc2wMQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" @@ -1362,13 +828,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-member-expression-literals@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.0.tgz#5251b4cce01eaf8314403d21aedb269d79f5e64b" - integrity sha512-WRpw5HL4Jhnxw8QARzRvwojp9MIE7Tdk3ez6vRyUk1MwgjJN0aNpRoXainLR5SgxmoXx/vsXGZ6OthP6t/RbUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-member-expression-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" @@ -1376,15 +835,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-modules-amd@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.0.tgz#09abd41e18dcf4fd479c598c1cef7bd39eb1337e" - integrity sha512-rWFhWbCJ9Wdmzln1NmSCqn7P0RAD+ogXG/bd9Kg5c7PKWkJtkiXmYsMBeXjDlzHpVTJ4I/hnjs45zX4dEv81xw== - dependencies: - "@babel/helper-module-transforms" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - babel-plugin-dynamic-import-node "^2.3.3" - "@babel/plugin-transform-modules-amd@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" @@ -1394,16 +844,6 @@ "@babel/helper-plugin-utils" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.0.tgz#add58e638c8ddc4875bd9a9ecb5c594613f6c922" - integrity sha512-Dzi+NWqyEotgzk/sb7kgQPJQf7AJkQBWsVp1N6JWc1lBVo0vkElUnGdr1PzUBmfsCCN5OOFya3RtpeHk15oLKQ== - dependencies: - "@babel/helper-module-transforms" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-simple-access" "^7.16.0" - babel-plugin-dynamic-import-node "^2.3.3" - "@babel/plugin-transform-modules-commonjs@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" @@ -1414,17 +854,6 @@ "@babel/helper-simple-access" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.0.tgz#a92cf240afeb605f4ca16670453024425e421ea4" - integrity sha512-yuGBaHS3lF1m/5R+6fjIke64ii5luRUg97N2wr+z1sF0V+sNSXPxXDdEEL/iYLszsN5VKxVB1IPfEqhzVpiqvg== - dependencies: - "@babel/helper-hoist-variables" "^7.16.0" - "@babel/helper-module-transforms" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-identifier" "^7.15.7" - babel-plugin-dynamic-import-node "^2.3.3" - "@babel/plugin-transform-modules-systemjs@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz#887cefaef88e684d29558c2b13ee0563e287c2d7" @@ -1436,14 +865,6 @@ "@babel/helper-validator-identifier" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.0.tgz#195f26c2ad6d6a391b70880effce18ce625e06a7" - integrity sha512-nx4f6no57himWiHhxDM5pjwhae5vLpTK2zCnDH8+wNLJy0TVER/LJRHl2bkt6w9Aad2sPD5iNNoUpY3X9sTGDg== - dependencies: - "@babel/helper-module-transforms" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-modules-umd@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" @@ -1452,13 +873,6 @@ "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-named-capturing-groups-regex@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.0.tgz#d3db61cc5d5b97986559967cd5ea83e5c32096ca" - integrity sha512-LogN88uO+7EhxWc8WZuQ8vxdSyVGxhkh8WTC3tzlT8LccMuQdA81e9SGV6zY7kY2LjDhhDOFdQVxdGwPyBCnvg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.0" - "@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252" @@ -1466,13 +880,6 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.7" -"@babel/plugin-transform-new-target@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.0.tgz#af823ab576f752215a49937779a41ca65825ab35" - integrity sha512-fhjrDEYv2DBsGN/P6rlqakwRwIp7rBGLPbrKxwh7oVt5NNkIhZVOY2GRV+ULLsQri1bDqwDWnU3vhlmx5B2aCw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-new-target@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" @@ -1480,14 +887,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-object-super@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.0.tgz#fb20d5806dc6491a06296ac14ea8e8d6fedda72b" - integrity sha512-fds+puedQHn4cPLshoHcR1DTMN0q1V9ou0mUjm8whx9pGcNvDrVVrgw+KJzzCaiTdaYhldtrUps8DWVMgrSEyg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.16.0" - "@babel/plugin-transform-object-super@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" @@ -1496,27 +895,13 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" -"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.16.0", "@babel/plugin-transform-parameters@^7.16.3": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.3.tgz#fa9e4c874ee5223f891ee6fa8d737f4766d31d15" - integrity sha512-3MaDpJrOXT1MZ/WCmkOFo7EtmVVC8H4EUZVrHvFOsmwkk4lOjQj8rzv8JKUZV4YoQKeoIgk07GO+acPU9IMu/w== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-parameters@^7.16.7": +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-property-literals@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.0.tgz#a95c552189a96a00059f6776dc4e00e3690c78d1" - integrity sha512-XLldD4V8+pOqX2hwfWhgwXzGdnDOThxaNTgqagOcpBgIxbUvpgU2FMvo5E1RyHbk756WYgdbS0T8y0Cj9FKkWQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-property-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" @@ -1524,13 +909,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-react-display-name@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.0.tgz#9a0ad8aa8e8790883a7bd2736f66229a58125676" - integrity sha512-FJFdJAqaCpndL+pIf0aeD/qlQwT7QXOvR6Cc8JPvNhKJBi2zc/DPc4g05Y3fbD/0iWAMQFGij4+Xw+4L/BMpTg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-react-display-name@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" @@ -1538,13 +916,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-react-jsx-development@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.0.tgz#1cb52874678d23ab11d0d16488d54730807303ef" - integrity sha512-qq65iSqBRq0Hr3wq57YG2AmW0H6wgTnIzpffTphrUWUgLCOK+zf1f7G0vuOiXrp7dU1qq+fQBoqZ3wCDAkhFzw== - dependencies: - "@babel/plugin-transform-react-jsx" "^7.16.0" - "@babel/plugin-transform-react-jsx-development@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" @@ -1552,18 +923,7 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.16.7" -"@babel/plugin-transform-react-jsx@^7.12.1", "@babel/plugin-transform-react-jsx@^7.12.12", "@babel/plugin-transform-react-jsx@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.0.tgz#55b797d4960c3de04e07ad1c0476e2bc6a4889f1" - integrity sha512-rqDgIbukZ44pqq7NIRPGPGNklshPkvlmvqjdx3OZcGPk4zGIenYkxDTvl3LsSL8gqcc3ZzGmXPE6hR/u/voNOw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-module-imports" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-jsx" "^7.16.0" - "@babel/types" "^7.16.0" - -"@babel/plugin-transform-react-jsx@^7.16.7": +"@babel/plugin-transform-react-jsx@^7.12.1", "@babel/plugin-transform-react-jsx@^7.12.12", "@babel/plugin-transform-react-jsx@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz#86a6a220552afd0e4e1f0388a68a372be7add0d4" integrity sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag== @@ -1574,14 +934,6 @@ "@babel/plugin-syntax-jsx" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/plugin-transform-react-pure-annotations@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.0.tgz#23db6ddf558d8abde41b8ad9d59f48ad5532ccab" - integrity sha512-NC/Bj2MG+t8Ef5Pdpo34Ay74X4Rt804h5y81PwOpfPtmAK3i6CizmQqwyBQzIepz1Yt8wNr2Z2L7Lu3qBMfZMA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-react-pure-annotations@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz#232bfd2f12eb551d6d7d01d13fe3f86b45eb9c67" @@ -1590,13 +942,6 @@ "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-regenerator@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.0.tgz#eaee422c84b0232d03aea7db99c97deeaf6125a4" - integrity sha512-JAvGxgKuwS2PihiSFaDrp94XOzzTUeDeOQlcKzVAyaPap7BnZXK/lvMDiubkPTdotPKOIZq9xWXWnggUMYiExg== - dependencies: - regenerator-transform "^0.14.2" - "@babel/plugin-transform-regenerator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" @@ -1604,13 +949,6 @@ dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.0.tgz#fff4b9dcb19e12619394bda172d14f2d04c0379c" - integrity sha512-Dgs8NNCehHSvXdhEhln8u/TtJxfVwGYCgP2OOr5Z3Ar+B+zXicEOKNTyc+eca2cuEOMtjW6m9P9ijOt8QdqWkg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-reserved-words@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" @@ -1618,19 +956,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-runtime@^7.16.0": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.4.tgz#f9ba3c7034d429c581e1bd41b4952f3db3c2c7e8" - integrity sha512-pru6+yHANMTukMtEZGC4fs7XPwg35v8sj5CIEmE+gEkFljFiVJxEWxx/7ZDkTK+iZRYo1bFXBtfIN95+K3cJ5A== - dependencies: - "@babel/helper-module-imports" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.4.0" - babel-plugin-polyfill-regenerator "^0.3.0" - semver "^6.3.0" - -"@babel/plugin-transform-runtime@^7.17.0": +"@babel/plugin-transform-runtime@^7.16.0", "@babel/plugin-transform-runtime@^7.17.0": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz#0a2e08b5e2b2d95c4b1d3b3371a2180617455b70" integrity sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A== @@ -1642,29 +968,14 @@ babel-plugin-polyfill-regenerator "^0.3.0" semver "^6.3.0" -"@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.0.tgz#090372e3141f7cc324ed70b3daf5379df2fa384d" - integrity sha512-iVb1mTcD8fuhSv3k99+5tlXu5N0v8/DPm2mO3WACLG6al1CGZH7v09HJyUb1TtYl/Z+KrM6pHSIJdZxP5A+xow== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-shorthand-properties@^7.16.7": +"@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.0.tgz#d21ca099bbd53ab307a8621e019a7bd0f40cdcfb" - integrity sha512-Ao4MSYRaLAQczZVp9/7E7QHsCuK92yHRrmVNRe/SlEJjhzivq0BSn8mEraimL8wizHZ3fuaHxKH0iwzI13GyGg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - -"@babel/plugin-transform-spread@^7.16.7": +"@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== @@ -1672,13 +983,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" -"@babel/plugin-transform-sticky-regex@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.0.tgz#c35ea31a02d86be485f6aa510184b677a91738fd" - integrity sha512-/ntT2NljR9foobKk4E/YyOSwcGUXtYWv5tinMK/3RkypyNBNdhHUaq6Orw5DWq9ZcNlS03BIlEALFeQgeVAo4Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-sticky-regex@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" @@ -1686,27 +990,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.0.tgz#a8eced3a8e7b8e2d40ec4ec4548a45912630d302" - integrity sha512-Rd4Ic89hA/f7xUSJQk5PnC+4so50vBoBfxjdQAdvngwidM8jYIBVxBZ/sARxD4e0yMXRbJVDrYf7dyRtIIKT6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-template-literals@^7.16.7": +"@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-typeof-symbol@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.0.tgz#8b19a244c6f8c9d668dca6a6f754ad6ead1128f2" - integrity sha512-++V2L8Bdf4vcaHi2raILnptTBjGEFxn5315YU+e8+EqXIucA+q349qWngCLpUYqqv233suJ6NOienIVUpS9cqg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-typeof-symbol@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" @@ -1714,15 +1004,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-typescript@^7.16.0": - version "7.16.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.1.tgz#cc0670b2822b0338355bc1b3d2246a42b8166409" - integrity sha512-NO4XoryBng06jjw/qWEU2LhcLJr1tWkhpMam/H4eas/CDKMX/b2/Ylb6EI256Y7+FVPCawwSM1rrJNOpDiz+Lg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-typescript" "^7.16.0" - "@babel/plugin-transform-typescript@^7.16.7": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" @@ -1732,13 +1013,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-typescript" "^7.16.7" -"@babel/plugin-transform-unicode-escapes@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.0.tgz#1a354064b4c45663a32334f46fa0cf6100b5b1f3" - integrity sha512-VFi4dhgJM7Bpk8lRc5CMaRGlKZ29W9C3geZjt9beuzSUrlJxsNwX7ReLwaL6WEvsOf2EQkyIJEPtF8EXjB/g2A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-unicode-escapes@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" @@ -1746,14 +1020,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-unicode-regex@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.0.tgz#293b80950177c8c85aede87cef280259fb995402" - integrity sha512-jHLK4LxhHjvCeZDWyA9c+P9XH1sOxRd1RO9xMtDVRAOND/PczPqizEtVdx4TQF/wyPaewqpT+tgQFYMnN/P94A== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.0" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-transform-unicode-regex@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" @@ -1762,87 +1028,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/preset-env@^7.12.11", "@babel/preset-env@^7.16.0": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.4.tgz#4f6ec33b2a3fe72d6bfdcdf3859500232563a2e3" - integrity sha512-v0QtNd81v/xKj4gNKeuAerQ/azeNn/G1B1qMLeXOcV8+4TWlD2j3NV1u8q29SDFBXx/NBq5kyEAO+0mpRgacjA== - dependencies: - "@babel/compat-data" "^7.16.4" - "@babel/helper-compilation-targets" "^7.16.3" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.2" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.0" - "@babel/plugin-proposal-async-generator-functions" "^7.16.4" - "@babel/plugin-proposal-class-properties" "^7.16.0" - "@babel/plugin-proposal-class-static-block" "^7.16.0" - "@babel/plugin-proposal-dynamic-import" "^7.16.0" - "@babel/plugin-proposal-export-namespace-from" "^7.16.0" - "@babel/plugin-proposal-json-strings" "^7.16.0" - "@babel/plugin-proposal-logical-assignment-operators" "^7.16.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.0" - "@babel/plugin-proposal-numeric-separator" "^7.16.0" - "@babel/plugin-proposal-object-rest-spread" "^7.16.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.16.0" - "@babel/plugin-proposal-private-methods" "^7.16.0" - "@babel/plugin-proposal-private-property-in-object" "^7.16.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.16.0" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.16.0" - "@babel/plugin-transform-async-to-generator" "^7.16.0" - "@babel/plugin-transform-block-scoped-functions" "^7.16.0" - "@babel/plugin-transform-block-scoping" "^7.16.0" - "@babel/plugin-transform-classes" "^7.16.0" - "@babel/plugin-transform-computed-properties" "^7.16.0" - "@babel/plugin-transform-destructuring" "^7.16.0" - "@babel/plugin-transform-dotall-regex" "^7.16.0" - "@babel/plugin-transform-duplicate-keys" "^7.16.0" - "@babel/plugin-transform-exponentiation-operator" "^7.16.0" - "@babel/plugin-transform-for-of" "^7.16.0" - "@babel/plugin-transform-function-name" "^7.16.0" - "@babel/plugin-transform-literals" "^7.16.0" - "@babel/plugin-transform-member-expression-literals" "^7.16.0" - "@babel/plugin-transform-modules-amd" "^7.16.0" - "@babel/plugin-transform-modules-commonjs" "^7.16.0" - "@babel/plugin-transform-modules-systemjs" "^7.16.0" - "@babel/plugin-transform-modules-umd" "^7.16.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.16.0" - "@babel/plugin-transform-new-target" "^7.16.0" - "@babel/plugin-transform-object-super" "^7.16.0" - "@babel/plugin-transform-parameters" "^7.16.3" - "@babel/plugin-transform-property-literals" "^7.16.0" - "@babel/plugin-transform-regenerator" "^7.16.0" - "@babel/plugin-transform-reserved-words" "^7.16.0" - "@babel/plugin-transform-shorthand-properties" "^7.16.0" - "@babel/plugin-transform-spread" "^7.16.0" - "@babel/plugin-transform-sticky-regex" "^7.16.0" - "@babel/plugin-transform-template-literals" "^7.16.0" - "@babel/plugin-transform-typeof-symbol" "^7.16.0" - "@babel/plugin-transform-unicode-escapes" "^7.16.0" - "@babel/plugin-transform-unicode-regex" "^7.16.0" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.16.0" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.4.0" - babel-plugin-polyfill-regenerator "^0.3.0" - core-js-compat "^3.19.1" - semver "^6.3.0" - -"@babel/preset-env@^7.16.11": +"@babel/preset-env@^7.12.11", "@babel/preset-env@^7.16.0", "@babel/preset-env@^7.16.11": version "7.16.11" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.11.tgz#5dd88fd885fae36f88fd7c8342475c9f0abe2982" integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== @@ -1941,19 +1127,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.12.10", "@babel/preset-react@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.0.tgz#f71d3e8dff5218478011df037fad52660ee6d82a" - integrity sha512-d31IFW2bLRB28uL1WoElyro8RH5l6531XfxMtCeCmp6RVAF1uTfxxUA0LH1tXl+psZdwfmIbwoG4U5VwgbhtLw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-transform-react-display-name" "^7.16.0" - "@babel/plugin-transform-react-jsx" "^7.16.0" - "@babel/plugin-transform-react-jsx-development" "^7.16.0" - "@babel/plugin-transform-react-pure-annotations" "^7.16.0" - -"@babel/preset-react@^7.16.7": +"@babel/preset-react@^7.12.10", "@babel/preset-react@^7.16.0", "@babel/preset-react@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.7.tgz#4c18150491edc69c183ff818f9f2aecbe5d93852" integrity sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA== @@ -1965,16 +1139,7 @@ "@babel/plugin-transform-react-jsx-development" "^7.16.7" "@babel/plugin-transform-react-pure-annotations" "^7.16.7" -"@babel/preset-typescript@^7.12.7": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.0.tgz#b0b4f105b855fb3d631ec036cdc9d1ffd1fa5eac" - integrity sha512-txegdrZYgO9DlPbv+9QOVpMnKbOtezsLHWsnsRF4AjbSIsVaujrq1qg8HK0mxQpWv0jnejt0yEoW1uWpvbrDTg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-transform-typescript" "^7.16.0" - -"@babel/preset-typescript@^7.16.7": +"@babel/preset-typescript@^7.12.7", "@babel/preset-typescript@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== @@ -1983,18 +1148,7 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" -"@babel/register@^7.12.1": - version "7.15.3" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.15.3.tgz#6b40a549e06ec06c885b2ec42c3dd711f55fe752" - integrity sha512-mj4IY1ZJkorClxKTImccn4T81+UKTo4Ux0+OFSV9hME1ooqS9UV+pJ6BjD0qXPK4T3XW/KNa79XByjeEMZz+fw== - dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.0" - source-map-support "^0.5.16" - -"@babel/register@^7.17.0": +"@babel/register@^7.12.1", "@babel/register@^7.17.0": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.17.0.tgz#8051e0b7cb71385be4909324f072599723a1f084" integrity sha512-UNZsMAZ7uKoGHo1HlEXfteEOYssf64n/PNLHGqOKq/bgYcu/4LrQWAHJwSCb3BRZK8Hi5gkJdRcwrGTO2wtRCg== @@ -2020,16 +1174,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.12.7", "@babel/template@^7.16.0", "@babel/template@^7.3.3": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" - integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/parser" "^7.16.0" - "@babel/types" "^7.16.0" - -"@babel/template@^7.16.7": +"@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== @@ -2038,70 +1183,23 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3", "@babel/traverse@^7.4.5": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" - integrity sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.0" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-hoist-variables" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/parser" "^7.16.3" - "@babel/types" "^7.16.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" - integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3", "@babel/traverse@^7.4.5": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" + integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== dependencies: "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.16.8" + "@babel/generator" "^7.17.3" "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-function-name" "^7.16.7" "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.16.10" - "@babel/types" "^7.16.8" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.0.tgz#3143e5066796408ccc880a33ecd3184f3e75cd30" - integrity sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.0" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.0" + "@babel/parser" "^7.17.3" "@babel/types" "^7.17.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" - integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.16.7", "@babel/types@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" - integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.17.0": +"@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== @@ -3790,7 +2888,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== -"@jridgewell/trace-mapping@^0.3.0": +"@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== @@ -9487,14 +8585,6 @@ babel-plugin-polyfill-corejs3@^0.1.0: "@babel/helper-define-polyfill-provider" "^0.1.5" core-js-compat "^3.8.1" -babel-plugin-polyfill-corejs3@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz#0b571f4cf3d67f911512f5c04842a7b8e8263087" - integrity sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.0" - core-js-compat "^3.18.0" - babel-plugin-polyfill-corejs3@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz#d66183bf10976ea677f4149a7fcc4d8df43d4060" @@ -9534,15 +8624,16 @@ babel-plugin-require-context-hook@^1.0.0: babel-plugin-syntax-jsx "^6.18.0" lodash "^4.17.11" -babel-plugin-styled-components@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz#0fac11402dc9db73698b55847ab1dc73f5197c54" - integrity sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw== +babel-plugin-styled-components@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.6.tgz#6f76c7f7224b7af7edc24a4910351948c691fc90" + integrity sha512-Sk+7o/oa2HfHv3Eh8sxoz75/fFvEdHsXV4grdeHufX0nauCmymlnN0rGhIvfpMQSJMvGutJ85gvCGea4iqmDpg== dependencies: "@babel/helper-annotate-as-pure" "^7.16.0" "@babel/helper-module-imports" "^7.16.0" babel-plugin-syntax-jsx "^6.18.0" lodash "^4.17.11" + picomatch "^2.3.0" babel-plugin-syntax-jsx@^6.18.0: version "6.18.0" @@ -11595,14 +10686,6 @@ copy-webpack-plugin@^6.0.2: serialize-javascript "^3.1.0" webpack-sources "^1.4.3" -core-js-compat@^3.18.0, core-js-compat@^3.19.1, core-js-compat@^3.8.1: - version "3.19.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.1.tgz#fe598f1a9bf37310d77c3813968e9f7c7bb99476" - integrity sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g== - dependencies: - browserslist "^4.17.6" - semver "7.0.0" - core-js-compat@^3.20.0, core-js-compat@^3.20.2: version "3.20.3" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.3.tgz#d71f85f94eb5e4bea3407412e549daa083d23bd6" @@ -11611,6 +10694,14 @@ core-js-compat@^3.20.0, core-js-compat@^3.20.2: browserslist "^4.19.1" semver "7.0.0" +core-js-compat@^3.8.1: + version "3.19.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.1.tgz#fe598f1a9bf37310d77c3813968e9f7c7bb99476" + integrity sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g== + dependencies: + browserslist "^4.17.6" + semver "7.0.0" + core-js-pure@^3.0.0, core-js-pure@^3.8.2: version "3.19.1" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.19.1.tgz#edffc1fc7634000a55ba05e95b3f0fe9587a5aa4" @@ -22969,12 +22060,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - -picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== @@ -23023,7 +22109,7 @@ pino@^6.11.2: quick-format-unescaped "^4.0.3" sonic-boom "^1.0.2" -pirates@^4.0.0, pirates@^4.0.1: +pirates@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== From be0bd5461f2c483af58585523129a3d59ea77ce0 Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Tue, 8 Mar 2022 14:31:38 +0000 Subject: [PATCH 060/140] [APM] set default value for Infrastructure View flag to false (#127127) --- x-pack/plugins/observability/server/ui_settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/server/ui_settings.ts b/x-pack/plugins/observability/server/ui_settings.ts index 866c8fe5432d5..4bc294871f5f9 100644 --- a/x-pack/plugins/observability/server/ui_settings.ts +++ b/x-pack/plugins/observability/server/ui_settings.ts @@ -59,7 +59,7 @@ export const uiSettings: Record Date: Tue, 8 Mar 2022 16:24:30 +0100 Subject: [PATCH 061/140] [Reporting] Copy updates for warning notifications (#126562) * updated test and copy for partial CSV warning message * updated report link copy, removed unnecessary copy and rather use new fragment in job warning * housekeeping: remove use of Fragment * also update the path link in the request report notification * added missing period Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../reporting/public/notifier/general_error.tsx | 6 +++--- .../reporting/public/notifier/job_failure.tsx | 6 +++--- .../reporting/public/notifier/job_success.tsx | 6 +++--- .../reporting/public/notifier/job_warning.tsx | 12 +++--------- .../public/notifier/job_warning_formulas.tsx | 6 +++--- .../public/notifier/job_warning_max_size.tsx | 6 +++--- .../reporting/public/notifier/report_link.tsx | 2 +- .../reporting_panel_content.tsx | 4 ++-- .../generate_csv/generate_csv.test.ts | 2 +- .../csv_searchsource/generate_csv/i18n_texts.ts | 2 +- 10 files changed, 23 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/reporting/public/notifier/general_error.tsx b/x-pack/plugins/reporting/public/notifier/general_error.tsx index 66fff4d00ceeb..4a66be603300f 100644 --- a/x-pack/plugins/reporting/public/notifier/general_error.tsx +++ b/x-pack/plugins/reporting/public/notifier/general_error.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { Fragment } from 'react'; +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { ThemeServiceStart, ToastInput } from 'src/core/public'; @@ -17,7 +17,7 @@ export const getGeneralErrorToast = ( theme: ThemeServiceStart ): ToastInput => ({ text: toMountPoint( - + <> {err.toString()} @@ -28,7 +28,7 @@ export const getGeneralErrorToast = ( id="xpack.reporting.publicNotifier.error.tryRefresh" defaultMessage="Try refreshing the page." /> - , + , { theme$: theme.theme$ } ), iconType: undefined, diff --git a/x-pack/plugins/reporting/public/notifier/job_failure.tsx b/x-pack/plugins/reporting/public/notifier/job_failure.tsx index 0f858333dae18..fff496a90cf64 100644 --- a/x-pack/plugins/reporting/public/notifier/job_failure.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_failure.tsx @@ -8,7 +8,7 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { Fragment } from 'react'; +import React from 'react'; import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import type { JobSummary, ManagementLinkFn } from '../../common/types'; @@ -29,7 +29,7 @@ export const getFailureToast = ( { theme$: theme.theme$ } ), text: toMountPoint( - + <>

-
, + , { theme$: theme.theme$ } ), iconType: undefined, diff --git a/x-pack/plugins/reporting/public/notifier/job_success.tsx b/x-pack/plugins/reporting/public/notifier/job_success.tsx index 76170a8a6c2c4..b5ff57b1c21cc 100644 --- a/x-pack/plugins/reporting/public/notifier/job_success.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_success.tsx @@ -6,7 +6,7 @@ */ import { FormattedMessage } from '@kbn/i18n-react'; -import React, { Fragment } from 'react'; +import React from 'react'; import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../common/types'; @@ -29,12 +29,12 @@ export const getSuccessToast = ( ), color: 'success', text: toMountPoint( - + <>

-
, + , { theme$: theme.theme$ } ), 'data-test-subj': 'completeReportSuccess', diff --git a/x-pack/plugins/reporting/public/notifier/job_warning.tsx b/x-pack/plugins/reporting/public/notifier/job_warning.tsx index 2ac10216f3f19..463bbd964273f 100644 --- a/x-pack/plugins/reporting/public/notifier/job_warning.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_warning.tsx @@ -6,7 +6,7 @@ */ import { FormattedMessage } from '@kbn/i18n-react'; -import React, { Fragment } from 'react'; +import React from 'react'; import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../common/types'; @@ -28,18 +28,12 @@ export const getWarningToast = ( { theme$: theme.theme$ } ), text: toMountPoint( - -

- -

+ <>

-
, + , { theme$: theme.theme$ } ), 'data-test-subj': 'completeReportWarning', diff --git a/x-pack/plugins/reporting/public/notifier/job_warning_formulas.tsx b/x-pack/plugins/reporting/public/notifier/job_warning_formulas.tsx index ae1d61b7bd4bf..8fcf0144006dc 100644 --- a/x-pack/plugins/reporting/public/notifier/job_warning_formulas.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_warning_formulas.tsx @@ -6,7 +6,7 @@ */ import { FormattedMessage } from '@kbn/i18n-react'; -import React, { Fragment } from 'react'; +import React from 'react'; import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../common/types'; @@ -28,7 +28,7 @@ export const getWarningFormulasToast = ( { theme$: theme.theme$ } ), text: toMountPoint( - + <>

-
, + , { theme$: theme.theme$ } ), 'data-test-subj': 'completeReportCsvFormulasWarning', diff --git a/x-pack/plugins/reporting/public/notifier/job_warning_max_size.tsx b/x-pack/plugins/reporting/public/notifier/job_warning_max_size.tsx index 6e3a68a9beee3..ccc133249505a 100644 --- a/x-pack/plugins/reporting/public/notifier/job_warning_max_size.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_warning_max_size.tsx @@ -6,7 +6,7 @@ */ import { FormattedMessage } from '@kbn/i18n-react'; -import React, { Fragment } from 'react'; +import React from 'react'; import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../common/types'; @@ -28,7 +28,7 @@ export const getWarningMaxSizeToast = ( { theme$: theme.theme$ } ), text: toMountPoint( - + <>

-
, + , { theme$: theme.theme$ } ), 'data-test-subj': 'completeReportMaxSizeWarning', diff --git a/x-pack/plugins/reporting/public/notifier/report_link.tsx b/x-pack/plugins/reporting/public/notifier/report_link.tsx index 9c3518163e5f4..d8f00777b04b8 100644 --- a/x-pack/plugins/reporting/public/notifier/report_link.tsx +++ b/x-pack/plugins/reporting/public/notifier/report_link.tsx @@ -21,7 +21,7 @@ export const ReportLink = ({ getUrl }: Props) => (
), diff --git a/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx index 1925bf377a370..d5f5a44837158 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx @@ -287,13 +287,13 @@ class ReportingPanelContentUi extends Component { text: toMountPoint( ), diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts index 4755d153666e4..66ee465ea142f 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts @@ -850,7 +850,7 @@ describe('error codes', () => { expect(errorCode).toBe('authentication_expired'); expect(warnings).toMatchInlineSnapshot(` Array [ - "This report contains partial CSV results because authentication expired before it could finish. Try exporting a smaller amount of data or increase your authentication timeout.", + "This report contains partial CSV results because the authentication token expired. Export a smaller amount of data or increase the timeout of the authentication token.", ] `); }); diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/i18n_texts.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/i18n_texts.ts index c994226c6a05c..72f6d96092e26 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/i18n_texts.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/i18n_texts.ts @@ -19,7 +19,7 @@ export const i18nTexts = { 'xpack.reporting.exportTypes.csv.generateCsv.authenticationExpired.partialResultsMessage', { defaultMessage: - 'This report contains partial CSV results because authentication expired before it could finish. Try exporting a smaller amount of data or increase your authentication timeout.', + 'This report contains partial CSV results because the authentication token expired. Export a smaller amount of data or increase the timeout of the authentication token.', } ), }, From 3dd05d673e538f434c164fefc28bc31dd0accdf8 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Tue, 8 Mar 2022 16:26:21 +0100 Subject: [PATCH 062/140] unksips cypress creation (#127024) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../cypress/integration/cases/creation.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts index d08f11a95b194..90ab1d098aef5 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts @@ -50,8 +50,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { CASES_URL } from '../../urls/navigation'; -// Flaky: https://github.com/elastic/kibana/issues/69847 -describe.skip('Cases', () => { +describe('Cases', () => { beforeEach(() => { cleanKibana(); createTimeline(getCase1().timeline).then((response) => From 5cab9e8bf46037b3c2dee7212eb5c76037f1333e Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Tue, 8 Mar 2022 16:29:38 +0100 Subject: [PATCH 063/140] [Cases] Add docs for new hooks (#127133) --- x-pack/plugins/cases/README.md | 82 +++++++++++++++++++ .../cases_context/use_cases_context.ts | 4 +- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/cases/README.md b/x-pack/plugins/cases/README.md index 2895d7d376666..dc551d6659737 100644 --- a/x-pack/plugins/cases/README.md +++ b/x-pack/plugins/cases/README.md @@ -38,6 +38,37 @@ This plugin provides cases management in Kibana cases: CasesUiStart; ``` +#### CasesContext setup + +To use any of the Cases UI hooks you must first initialize `CasesContext` in your plugin. + +Without a `CasesContext` the hooks won't work and won't be able to render. + +`CasesContext` works a bridge between your plugin and the Cases UI. It effectively renders +the Cases UI. + +To initialize the `CasesContext` you can use this code: + + +```ts +// somewhere high on your plugin render tree + + {/* or something similar */} + +``` + +props: + +| prop | type | description | +|-----------------------|-----------------|----------------------------------------------------------------| +| PLUGIN_CASES_OWNER_ID | `string` | The owner string for your plugin. e.g: securitySolution | +| CASES_USER_CAN_CRUD | `boolean` | Defines if the user has access to cases to CRUD | +| CASES_FEATURES | `CasesFeatures` | `CasesFeatures` object defining the features to enable/disable | + #### Cases UI Methods - From the UI component, get the component from the `useKibana` hook start services @@ -140,6 +171,57 @@ Arguments: UI component: ![Recent Cases Component][recent-cases-img] +### hooks + + +#### getUseCasesAddToNewCaseFlyout + +Returns an object containing two methods: `open` and `close` to either open or close the add to new case flyout. +You can use this hook to prompt the user to create a new case with some attachments directly attached to it. e.g.: alerts or text comments. + + +Arguments: + +| Property | Description | +| ----------------- | ------------------------------------------------------------------------------------------------------------------ | +| userCanCrud | `boolean;` user permissions to crud | +| onClose | `() => void;` callback when create case is canceled | +| onSuccess | `(theCase: Case) => Promise;` callback passing newly created case after pushCaseToExternalService is called | +| afterCaseCreated? | `(theCase: Case) => Promise;` callback passing newly created case before pushCaseToExternalService is called | +| attachments? | `CaseAttachments`; array of `SupportedCaseAttachment` (see types) that will be attached to the newly created case | + +returns: an object with `open` and `close` methods to open or close the flyout. + +`open()` and `close()` don't take any arguments. They will open or close the flyout at will. + +#### `getUseCasesAddToExistingCaseModal` + +Returns an object containing two methods: `open` and `close` to either open or close the case selector modal. + +You can use this hook to prompt the user to select a case and get the selected case. You can also pass attachments directly and have them attached to the selected case after selection. e.g.: alerts or text comments. + + +Arguments: + +| Property | Description | +| --------------- | ------------------------------------------------------------------------------------------------- | +| onRowClick | (theCase?: Case) => void; callback for row click, passing case in row | +| updateCase? | (theCase: Case) => void; callback after case has been updated | +| onClose? | `() => void` called when the modal is closed without selecting a case | +| attachments? | `CaseAttachments`; array of `SupportedCaseAttachment` (see types) that will be attached to the newly created case | + +### helpers + +#### getRuleIdFromEvent + +Returns an object with a rule `id` and `name` of the event passed. This helper method is necessary to bridge the gap between previous events schema and new ones. + +Arguments: + +| property | description | +|----------|----------------------------------------------------------------------------------------------| +| event | Event containing an `ecs` attribute with ecs data and a `data` attribute with `nonEcs` data. | + diff --git a/x-pack/plugins/cases/public/components/cases_context/use_cases_context.ts b/x-pack/plugins/cases/public/components/cases_context/use_cases_context.ts index 2244145f71111..8f93dfe4a02e6 100644 --- a/x-pack/plugins/cases/public/components/cases_context/use_cases_context.ts +++ b/x-pack/plugins/cases/public/components/cases_context/use_cases_context.ts @@ -12,7 +12,9 @@ export const useCasesContext = () => { const casesContext = useContext(CasesContext); if (!casesContext) { - throw new Error('useCasesContext must be used within a CasesProvider and have a defined value'); + throw new Error( + 'useCasesContext must be used within a CasesProvider and have a defined value. See https://github.com/elastic/kibana/blob/main/x-pack/plugins/cases/README.md#cases-ui' + ); } return casesContext; From a76fef102e65de23089f01633701ae5d9f6b517e Mon Sep 17 00:00:00 2001 From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com> Date: Tue, 8 Mar 2022 10:53:56 -0500 Subject: [PATCH 064/140] Stop showing the warning toast during security solution cypress tests (#127107) --- x-pack/test/security_solution_cypress/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts index fde617bcf1ce4..f9665c2a6729a 100644 --- a/x-pack/test/security_solution_cypress/config.ts +++ b/x-pack/test/security_solution_cypress/config.ts @@ -35,6 +35,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { serverArgs: [ ...xpackFunctionalTestsConfig.get('kbnTestServer.serverArgs'), '--csp.strict=false', + '--csp.warnLegacyBrowsers=false', // define custom kibana server args here `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, // retrieve rules from the filesystem but not from fleet for Cypress tests From ed4c19c692beed1749742c6fad280e2d32117498 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Tue, 8 Mar 2022 11:04:28 -0500 Subject: [PATCH 065/140] Use debounce instead of async url update to remove app state from URL (#127083) --- .../lib/sync_dashboard_url_state.ts | 92 +++++++------------ .../apps/dashboard/dashboard_save.ts | 4 + .../apps/dashboard/dashboard_state.ts | 2 + .../functional/page_objects/dashboard_page.ts | 9 ++ 4 files changed, 50 insertions(+), 57 deletions(-) diff --git a/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts b/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts index 392b37bb4d8e0..227430142325a 100644 --- a/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts +++ b/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts @@ -7,6 +7,7 @@ */ import _ from 'lodash'; +import { debounceTime } from 'rxjs/operators'; import { migrateAppState } from '.'; import { DashboardSavedObject } from '../..'; @@ -25,8 +26,6 @@ import { convertSavedPanelsToPanelMap } from './convert_dashboard_panels'; type SyncDashboardUrlStateProps = DashboardBuildContext & { savedDashboard: DashboardSavedObject }; -let awaitingRemoval = false; - export const syncDashboardUrlState = ({ dispatchDashboardStateChange, getLatestDashboardState, @@ -36,14 +35,44 @@ export const syncDashboardUrlState = ({ savedDashboard, kibanaVersion, }: SyncDashboardUrlStateProps) => { + /** + * Loads any dashboard state from the URL, and removes the state from the URL. + */ + const loadAndRemoveDashboardState = (): Partial => { + const rawAppStateInUrl = kbnUrlStateStorage.get(DASHBOARD_STATE_STORAGE_KEY); + if (!rawAppStateInUrl) return {}; + + let panelsMap: DashboardPanelMap = {}; + if (rawAppStateInUrl.panels && rawAppStateInUrl.panels.length > 0) { + const rawState = migrateAppState(rawAppStateInUrl, kibanaVersion, usageCollection); + panelsMap = convertSavedPanelsToPanelMap(rawState.panels); + } + + const migratedQuery = rawAppStateInUrl.query + ? migrateLegacyQuery(rawAppStateInUrl.query) + : undefined; + + const nextUrl = replaceUrlHashQuery(window.location.href, (query) => { + delete query[DASHBOARD_STATE_STORAGE_KEY]; + return query; + }); + kbnUrlStateStorage.kbnUrlControls.update(nextUrl, true); + + return { + ..._.omit(rawAppStateInUrl, ['panels', 'query']), + ...(migratedQuery ? { query: migratedQuery } : {}), + ...(rawAppStateInUrl.panels ? { panels: panelsMap } : {}), + }; + }; + // load initial state before subscribing to avoid state removal triggering update. - const loadDashboardStateProps = { kbnUrlStateStorage, usageCollection, kibanaVersion }; - const initialDashboardStateFromUrl = loadDashboardUrlState(loadDashboardStateProps); + const initialDashboardStateFromUrl = loadAndRemoveDashboardState(); const appStateSubscription = kbnUrlStateStorage .change$(DASHBOARD_STATE_STORAGE_KEY) + .pipe(debounceTime(10)) // debounce URL updates so react has time to unsubscribe when changing URLs .subscribe(() => { - const stateFromUrl = loadDashboardUrlState(loadDashboardStateProps); + const stateFromUrl = loadAndRemoveDashboardState(); const updatedDashboardState = { ...getLatestDashboardState(), ...stateFromUrl }; applyDashboardFilterState({ @@ -57,57 +86,6 @@ export const syncDashboardUrlState = ({ dispatchDashboardStateChange(setDashboardState(updatedDashboardState)); }); - const stopWatchingAppStateInUrl = () => { - appStateSubscription.unsubscribe(); - }; + const stopWatchingAppStateInUrl = () => appStateSubscription.unsubscribe(); return { initialDashboardStateFromUrl, stopWatchingAppStateInUrl }; }; - -interface LoadDashboardUrlStateProps { - kibanaVersion: DashboardBuildContext['kibanaVersion']; - usageCollection: DashboardBuildContext['usageCollection']; - kbnUrlStateStorage: DashboardBuildContext['kbnUrlStateStorage']; -} - -/** - * Loads any dashboard state from the URL, and removes the state from the URL. - */ -const loadDashboardUrlState = ({ - kibanaVersion, - usageCollection, - kbnUrlStateStorage, -}: LoadDashboardUrlStateProps): Partial => { - const rawAppStateInUrl = kbnUrlStateStorage.get(DASHBOARD_STATE_STORAGE_KEY); - if (!rawAppStateInUrl) return {}; - - let panelsMap: DashboardPanelMap = {}; - if (rawAppStateInUrl.panels && rawAppStateInUrl.panels.length > 0) { - const rawState = migrateAppState(rawAppStateInUrl, kibanaVersion, usageCollection); - panelsMap = convertSavedPanelsToPanelMap(rawState.panels); - } - - const migratedQuery = rawAppStateInUrl.query - ? migrateLegacyQuery(rawAppStateInUrl.query) - : undefined; - - // remove state from URL - if (!awaitingRemoval) { - awaitingRemoval = true; - kbnUrlStateStorage.kbnUrlControls.updateAsync((nextUrl) => { - awaitingRemoval = false; - if (nextUrl.includes(DASHBOARD_STATE_STORAGE_KEY)) { - return replaceUrlHashQuery(nextUrl, (query) => { - delete query[DASHBOARD_STATE_STORAGE_KEY]; - return query; - }); - } - return nextUrl; - }, true); - } - - return { - ..._.omit(rawAppStateInUrl, ['panels', 'query']), - ...(migratedQuery ? { query: migratedQuery } : {}), - ...(rawAppStateInUrl.panels ? { panels: panelsMap } : {}), - }; -}; diff --git a/test/functional/apps/dashboard/dashboard_save.ts b/test/functional/apps/dashboard/dashboard_save.ts index dce59744db305..4ab8633a5619b 100644 --- a/test/functional/apps/dashboard/dashboard_save.ts +++ b/test/functional/apps/dashboard/dashboard_save.ts @@ -58,6 +58,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // wait till it finishes reloading or it might reload the url after simulating the // dashboard landing page click. await PageObjects.header.waitUntilLoadingHasFinished(); + + // after saving a new dashboard, the app state must be removed + await await PageObjects.dashboard.expectAppStateRemovedFromURL(); + await PageObjects.dashboard.gotoDashboardLandingPage(); await listingTable.searchAndExpectItemsCount('dashboard', dashboardName, 2); diff --git a/test/functional/apps/dashboard/dashboard_state.ts b/test/functional/apps/dashboard/dashboard_state.ts index 0cc0fa4806482..d3643753c8e83 100644 --- a/test/functional/apps/dashboard/dashboard_state.ts +++ b/test/functional/apps/dashboard/dashboard_state.ts @@ -195,12 +195,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('for query parameter with soft refresh', async function () { await changeQuery(false, 'hi:goodbye'); + await PageObjects.dashboard.expectAppStateRemovedFromURL(); }); it('for query parameter with hard refresh', async function () { await changeQuery(true, 'hi:hello'); await queryBar.clearQuery(); await queryBar.clickQuerySubmitButton(); + await PageObjects.dashboard.expectAppStateRemovedFromURL(); }); it('for panel size parameters', async function () { diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index acda3d5b8b02d..d2376d84bdccb 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -9,6 +9,8 @@ export const PIE_CHART_VIS_NAME = 'Visualization PieChart'; export const AREA_CHART_VIS_NAME = 'Visualization漢字 AreaChart'; export const LINE_CHART_VIS_NAME = 'Visualization漢字 LineChart'; + +import expect from '@kbn/expect'; import { FtrService } from '../ftr_provider_context'; interface SaveDashboardOptions { @@ -51,6 +53,13 @@ export class DashboardPageObject extends FtrService { await this.common.navigateToApp('dashboard'); } + public async expectAppStateRemovedFromURL() { + this.retry.try(async () => { + const url = await this.browser.getCurrentUrl(); + expect(url.indexOf('_a')).to.be(-1); + }); + } + public async preserveCrossAppState() { const url = await this.browser.getCurrentUrl(); await this.browser.get(url, false); From 192309fc2722f6178a3edaabc17e6099457fa046 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 8 Mar 2022 08:21:38 -0800 Subject: [PATCH 066/140] Typescript upgrade to 4.6.2 (#126935) --- package.json | 12 +- .../actions/server/action_type_registry.ts | 4 +- .../geo/geojson_importer/geojson_importer.ts | 23 +++- .../osquery/cypress/support/commands.ts | 36 ------ .../plugins/osquery/cypress/support/index.ts | 16 ++- .../reporting/server/routes/lib/jobs_query.ts | 3 +- .../server/browsers/extract/extract.ts | 2 +- .../server/browsers/extract/extract_error.ts | 4 +- .../view/side_effect_simulator_factory.ts | 1 + yarn.lock | 118 ++++++++++-------- 10 files changed, 101 insertions(+), 118 deletions(-) delete mode 100644 x-pack/plugins/osquery/cypress/support/commands.ts diff --git a/package.json b/package.json index f3900fdadb40c..ba4ff174c4c42 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", "**/refractor/prismjs": "~1.27.0", "**/trim": "1.0.1", - "**/typescript": "4.5.3", + "**/typescript": "4.6.2", "**/underscore": "^1.13.1", "globby/fast-glob": "3.2.7", "puppeteer/node-fetch": "^2.6.7" @@ -567,7 +567,7 @@ "@types/js-levenshtein": "^1.1.0", "@types/js-search": "^1.4.0", "@types/js-yaml": "^3.11.1", - "@types/jsdom": "^16.2.13", + "@types/jsdom": "^16.2.14", "@types/json-stable-stringify": "^1.0.32", "@types/json5": "^0.0.30", "@types/kbn__ace": "link:bazel-bin/packages/kbn-ace/npm_module_types", @@ -714,9 +714,9 @@ "@types/yargs": "^15.0.0", "@types/yauzl": "^2.9.1", "@types/zen-observable": "^0.8.0", - "@typescript-eslint/eslint-plugin": "^5.6.0", - "@typescript-eslint/parser": "^5.6.0", - "@typescript-eslint/typescript-estree": "^5.6.0", + "@typescript-eslint/eslint-plugin": "^5.13.0", + "@typescript-eslint/parser": "^5.13.0", + "@typescript-eslint/typescript-estree": "^5.13.0", "@yarnpkg/lockfile": "^1.1.0", "abab": "^2.0.4", "aggregate-error": "^3.1.0", @@ -889,7 +889,7 @@ "ts-loader": "^7.0.5", "ts-morph": "^13.0.2", "tsd": "^0.13.1", - "typescript": "4.5.3", + "typescript": "4.6.2", "unlazy-loader": "^0.1.3", "url-loader": "^2.2.0", "val-loader": "^1.1.1", diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 76b360ce8b17f..9f8d2dec2128d 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -122,7 +122,7 @@ export class ActionTypeRegistry { ) ); } - this.actionTypes.set(actionType.id, { ...actionType } as ActionType); + this.actionTypes.set(actionType.id, { ...actionType } as unknown as ActionType); this.taskManager.registerTaskDefinitions({ [`actions:${actionType.id}`]: { title: actionType.name, @@ -141,7 +141,7 @@ export class ActionTypeRegistry { // No need to notify usage on basic action types if (actionType.minimumLicenseRequired !== 'basic') { this.licensing.featureUsage.register( - getActionTypeFeatureUsageName(actionType as ActionType), + getActionTypeFeatureUsageName(actionType as unknown as ActionType), actionType.minimumLicenseRequired ); } diff --git a/x-pack/plugins/file_upload/public/importer/geo/geojson_importer/geojson_importer.ts b/x-pack/plugins/file_upload/public/importer/geo/geojson_importer/geojson_importer.ts index 8cc39bf0eebd1..c3de1ac2e9491 100644 --- a/x-pack/plugins/file_upload/public/importer/geo/geojson_importer/geojson_importer.ts +++ b/x-pack/plugins/file_upload/public/importer/geo/geojson_importer/geojson_importer.ts @@ -14,8 +14,15 @@ import { AbstractGeoFileImporter } from '../abstract_geo_file_importer'; export const GEOJSON_FILE_TYPES = ['.json', '.geojson']; +interface LoaderBatch { + bytesUsed?: number; + batchType?: string; + container?: Feature; + data?: Feature[]; +} + export class GeoJsonImporter extends AbstractGeoFileImporter { - private _iterator?: Iterator; + private _iterator?: AsyncIterator; private _prevBatchLastFeature?: Feature; protected async _readNext(prevTotalFeaturesRead: number, prevTotalBytesRead: number) { @@ -49,24 +56,28 @@ export class GeoJsonImporter extends AbstractGeoFileImporter { return results; } - if ('bytesUsed' in batch) { + if (batch.bytesUsed) { results.bytesRead = batch.bytesUsed - prevTotalBytesRead; } - const features: unknown[] = this._prevBatchLastFeature ? [this._prevBatchLastFeature] : []; + const features: Feature[] = this._prevBatchLastFeature ? [this._prevBatchLastFeature] : []; this._prevBatchLastFeature = undefined; const isLastBatch = batch.batchType === 'root-object-batch-complete'; if (isLastBatch) { // Handle single feature geoJson if (featureIndex === 0) { - features.push(batch.container); + if (batch.container) { + features.push(batch.container); + } } } else { - features.push(...batch.data); + if (batch.data) { + features.push(...batch.data); + } } for (let i = 0; i < features.length; i++) { - const feature = features[i] as Feature; + const feature = features[i]; if (!isLastBatch && i === features.length - 1) { // Do not process last feature until next batch is read, features on batch boundary may be incomplete. this._prevBatchLastFeature = feature; diff --git a/x-pack/plugins/osquery/cypress/support/commands.ts b/x-pack/plugins/osquery/cypress/support/commands.ts deleted file mode 100644 index a0f3744f992b8..0000000000000 --- a/x-pack/plugins/osquery/cypress/support/commands.ts +++ /dev/null @@ -1,36 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) - -Cypress.Commands.add('getBySel', (selector, ...args) => - cy.get(`[data-test-subj=${selector}]`, ...args) -); diff --git a/x-pack/plugins/osquery/cypress/support/index.ts b/x-pack/plugins/osquery/cypress/support/index.ts index 5fe2342cc5c72..15e2b2516cbe4 100644 --- a/x-pack/plugins/osquery/cypress/support/index.ts +++ b/x-pack/plugins/osquery/cypress/support/index.ts @@ -22,27 +22,25 @@ // https://on.cypress.io/configuration // *********************************************************** +// force ESM in this module +export {}; + // eslint-disable-next-line import/no-extraneous-dependencies import 'cypress-react-selector'; -// Import commands.js using ES2015 syntax: -import './commands'; // import './coverage'; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace Cypress { interface Chainable { - getBySel: typeof cy.get; + getBySel(...args: Parameters): Chainable>; } } } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function getBySel(selector: string, ...args: any[]) { - return cy.get(`[data-test-subj=${selector}]`, ...args); -} - -Cypress.Commands.add('getBySel', getBySel); +Cypress.Commands.add('getBySel', (selector, ...args) => + cy.get(`[data-test-subj="${selector}"]`, ...args) +); // Alternatively you can use CommonJS syntax: // require('./commands') diff --git a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts b/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts index 27126baad021d..af679b403cbaf 100644 --- a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts +++ b/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts @@ -15,7 +15,6 @@ import { import { errors } from '@elastic/elasticsearch'; import { i18n } from '@kbn/i18n'; import { ElasticsearchClient } from 'src/core/server'; -import { PromiseType } from 'utility-types'; import { ReportingCore } from '../../'; import { REPORTING_SYSTEM_INDEX } from '../../../common/constants'; import { ReportApiJSON, ReportSource } from '../../../common/types'; @@ -61,7 +60,7 @@ export function jobsQueryFactory(reportingCore: ReportingCore): JobsQueryFactory } async function execQuery< - T extends (client: ElasticsearchClient) => Promise> | undefined> + T extends (client: ElasticsearchClient) => Promise> | undefined> >(callback: T): Promise> | undefined> { try { const { asInternalUser: client } = await reportingCore.getEsClient(); diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/extract.ts b/x-pack/plugins/screenshotting/server/browsers/extract/extract.ts index ccdfb1eaad5c2..0fab91fc9861d 100644 --- a/x-pack/plugins/screenshotting/server/browsers/extract/extract.ts +++ b/x-pack/plugins/screenshotting/server/browsers/extract/extract.ts @@ -18,7 +18,7 @@ export async function extract(archivePath: string, targetPath: string) { unpacker = unzip; break; default: - throw new ExtractError(`Unable to unpack filetype: ${fileType}`); + throw new ExtractError(new Error(`Unable to unpack filetype: ${fileType}`)); } await unpacker(archivePath, targetPath); diff --git a/x-pack/plugins/screenshotting/server/browsers/extract/extract_error.ts b/x-pack/plugins/screenshotting/server/browsers/extract/extract_error.ts index 04a915c2afc93..2e8b85b217152 100644 --- a/x-pack/plugins/screenshotting/server/browsers/extract/extract_error.ts +++ b/x-pack/plugins/screenshotting/server/browsers/extract/extract_error.ts @@ -6,9 +6,9 @@ */ export class ExtractError extends Error { - public readonly cause: string; + public readonly cause: Error; - constructor(cause: string, message = 'Failed to extract the browser archive') { + constructor(cause: Error, message = 'Failed to extract the browser archive') { super(message); this.message = message; this.name = this.constructor.name; diff --git a/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts b/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts index 1ce268ee74a5a..268b75b4a3fbb 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts +++ b/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts @@ -107,6 +107,7 @@ export const sideEffectSimulatorFactory: () => SideEffectSimulator = () => { contentRect, borderBoxSize: [{ inlineSize: 0, blockSize: 0 }], contentBoxSize: [{ inlineSize: 0, blockSize: 0 }], + devicePixelContentBoxSize: [], }, ]; this.callback(entries, this); diff --git a/yarn.lock b/yarn.lock index 2bc338e6bad46..c76e36d61897f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5858,10 +5858,10 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.1.tgz#5c6f4a1eabca84792fbd916f0cb40847f123c656" integrity sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA== -"@types/jsdom@^16.2.13": - version "16.2.13" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.13.tgz#126c8b7441b159d6234610a48de77b6066f1823f" - integrity sha512-8JQCjdeAidptSsOcRWk2iTm9wCcwn9l+kRG6k5bzUacrnm1ezV4forq0kWjUih/tumAeoG+OspOvQEbbRucBTw== +"@types/jsdom@^16.2.14": + version "16.2.14" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.14.tgz#26fe9da6a8870715b154bb84cd3b2e53433d8720" + integrity sha512-6BAy1xXEmMuHeAJ4Fv4yXKwBDTGTOseExKE3OaHiNycdHdZw59KfYzrt0DkDluvwmik1HRt6QS7bImxUmpSy+w== dependencies: "@types/node" "*" "@types/parse5" "*" @@ -7044,13 +7044,14 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== -"@typescript-eslint/eslint-plugin@^5.6.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz#12d54709f8ea1da99a01d8a992cd0474ad0f0aa9" - integrity sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg== +"@typescript-eslint/eslint-plugin@^5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.13.0.tgz#2809052b85911ced9c54a60dac10e515e9114497" + integrity sha512-vLktb2Uec81fxm/cfz2Hd6QaWOs8qdmVAZXLdOBX6JFJDhf6oDZpMzZ4/LZ6SFM/5DgDcxIMIvy3F+O9yZBuiQ== dependencies: - "@typescript-eslint/experimental-utils" "5.7.0" - "@typescript-eslint/scope-manager" "5.7.0" + "@typescript-eslint/scope-manager" "5.13.0" + "@typescript-eslint/type-utils" "5.13.0" + "@typescript-eslint/utils" "5.13.0" debug "^4.3.2" functional-red-black-tree "^1.0.1" ignore "^5.1.8" @@ -7058,18 +7059,6 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz#2b1633e6613c3238036156f70c32634843ad034f" - integrity sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A== - dependencies: - "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.7.0" - "@typescript-eslint/types" "5.7.0" - "@typescript-eslint/typescript-estree" "5.7.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - "@typescript-eslint/experimental-utils@^4.0.1": version "4.31.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.2.tgz#98727a9c1e977dd5d20c8705e69cd3c2a86553fa" @@ -7082,14 +7071,14 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/parser@^5.6.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.7.0.tgz#4dca6de463d86f02d252e681136a67888ea3b181" - integrity sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g== +"@typescript-eslint/parser@^5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.13.0.tgz#0394ed8f2f849273c0bf4b811994d177112ced5c" + integrity sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg== dependencies: - "@typescript-eslint/scope-manager" "5.7.0" - "@typescript-eslint/types" "5.7.0" - "@typescript-eslint/typescript-estree" "5.7.0" + "@typescript-eslint/scope-manager" "5.13.0" + "@typescript-eslint/types" "5.13.0" + "@typescript-eslint/typescript-estree" "5.13.0" debug "^4.3.2" "@typescript-eslint/scope-manager@4.31.2": @@ -7100,23 +7089,32 @@ "@typescript-eslint/types" "4.31.2" "@typescript-eslint/visitor-keys" "4.31.2" -"@typescript-eslint/scope-manager@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz#70adf960e5a58994ad50438ba60d98ecadd79452" - integrity sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA== +"@typescript-eslint/scope-manager@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz#cf6aff61ca497cb19f0397eea8444a58f46156b6" + integrity sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA== dependencies: - "@typescript-eslint/types" "5.7.0" - "@typescript-eslint/visitor-keys" "5.7.0" + "@typescript-eslint/types" "5.13.0" + "@typescript-eslint/visitor-keys" "5.13.0" + +"@typescript-eslint/type-utils@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.13.0.tgz#b0efd45c85b7bab1125c97b752cab3a86c7b615d" + integrity sha512-/nz7qFizaBM1SuqAKb7GLkcNn2buRdDgZraXlkhz+vUGiN1NZ9LzkA595tHHeduAiS2MsHqMNhE2zNzGdw43Yg== + dependencies: + "@typescript-eslint/utils" "5.13.0" + debug "^4.3.2" + tsutils "^3.21.0" "@typescript-eslint/types@4.31.2": version "4.31.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.31.2.tgz#2aea7177d6d744521a168ed4668eddbd912dfadf" integrity sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w== -"@typescript-eslint/types@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.7.0.tgz#2d4cae0105ba7d08bffa69698197a762483ebcbe" - integrity sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA== +"@typescript-eslint/types@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.13.0.tgz#da1de4ae905b1b9ff682cab0bed6b2e3be9c04e5" + integrity sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg== "@typescript-eslint/typescript-estree@4.31.2": version "4.31.2" @@ -7131,19 +7129,31 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.7.0", "@typescript-eslint/typescript-estree@^5.6.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz#968fad899050ccce4f08a40cd5fabc0798525006" - integrity sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg== +"@typescript-eslint/typescript-estree@5.13.0", "@typescript-eslint/typescript-estree@^5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz#b37c07b748ff030a3e93d87c842714e020b78141" + integrity sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA== dependencies: - "@typescript-eslint/types" "5.7.0" - "@typescript-eslint/visitor-keys" "5.7.0" + "@typescript-eslint/types" "5.13.0" + "@typescript-eslint/visitor-keys" "5.13.0" debug "^4.3.2" globby "^11.0.4" is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/utils@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.13.0.tgz#2328feca700eb02837298339a2e49c46b41bd0af" + integrity sha512-+9oHlPWYNl6AwwoEt5TQryEHwiKRVjz7Vk6kaBeD3/kwHE5YqTGHtm/JZY8Bo9ITOeKutFaXnBlMgSATMJALUQ== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.13.0" + "@typescript-eslint/types" "5.13.0" + "@typescript-eslint/typescript-estree" "5.13.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + "@typescript-eslint/visitor-keys@4.31.2": version "4.31.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz#7d5b4a4705db7fe59ecffb273c1d082760f635cc" @@ -7152,12 +7162,12 @@ "@typescript-eslint/types" "4.31.2" eslint-visitor-keys "^2.0.0" -"@typescript-eslint/visitor-keys@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz#e05164239eb7cb8aa9fa06c516ede480ce260178" - integrity sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg== +"@typescript-eslint/visitor-keys@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz#f45ff55bcce16403b221ac9240fbeeae4764f0fd" + integrity sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g== dependencies: - "@typescript-eslint/types" "5.7.0" + "@typescript-eslint/types" "5.13.0" eslint-visitor-keys "^3.0.0" "@ungap/promise-all-settled@1.1.2": @@ -28178,10 +28188,10 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -typescript@4.5.3, typescript@^3.3.3333, typescript@^3.5.3, typescript@^4.3.5, typescript@~4.4.2: - version "4.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c" - integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ== +typescript@4.6.2, typescript@^3.3.3333, typescript@^3.5.3, typescript@^4.3.5, typescript@~4.4.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" + integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== ua-parser-js@^0.7.18: version "0.7.24" From 6c4e208ce7f4508b05d4cca0732bc10b3c87de6a Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 8 Mar 2022 10:31:59 -0600 Subject: [PATCH 067/140] make getViewUnderlyingDataArgs synchronous --- x-pack/plugins/lens/public/embeddable/embeddable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 27cde618e499e..50473fe8aa286 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -157,7 +157,7 @@ const getExpressionFromDocument = async ( }; }; -async function getViewUnderlyingDataArgs({ +function getViewUnderlyingDataArgs({ activeDatasource, activeDatasourceState, activeData, @@ -715,7 +715,7 @@ export class Embeddable ); } - const viewUnderlyingDataArgs = await getViewUnderlyingDataArgs({ + const viewUnderlyingDataArgs = getViewUnderlyingDataArgs({ activeDatasource: this.activeDataInfo.activeDatasource!, activeDatasourceState: this.activeDataInfo.activeDatasourceState, activeData: this.activeDataInfo.activeData, From 3864f63ccea1fe8aa27bc43fd9a6b9625cb19f6f Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 8 Mar 2022 09:40:49 -0700 Subject: [PATCH 068/140] [SO migrations] exit early if cluster routing allocation is disabled (#126612) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/saved_objects/migrations/README.md | 24 ++- .../saved_objects/migrations/actions/index.ts | 6 + .../actions/initialize_action.test.ts | 37 +++++ .../migrations/actions/initialize_action.ts | 77 +++++++++ .../actions/integration_tests/actions.test.ts | 81 ++++++++- ...luster_routing_allocation_disabled.test.ts | 155 ++++++++++++++++++ .../migrations/kibana_migrator.test.ts | 15 +- .../migrations/model/model.test.ts | 11 ++ .../saved_objects/migrations/model/model.ts | 21 ++- .../server/saved_objects/migrations/next.ts | 2 +- 10 files changed, 416 insertions(+), 13 deletions(-) create mode 100644 src/core/server/saved_objects/migrations/actions/initialize_action.test.ts create mode 100644 src/core/server/saved_objects/migrations/actions/initialize_action.ts create mode 100644 src/core/server/saved_objects/migrations/integration_tests/cluster_routing_allocation_disabled.test.ts diff --git a/src/core/server/saved_objects/migrations/README.md b/src/core/server/saved_objects/migrations/README.md index 60bf84eef87a6..d8382e4f0e061 100644 --- a/src/core/server/saved_objects/migrations/README.md +++ b/src/core/server/saved_objects/migrations/README.md @@ -133,6 +133,14 @@ is left out of the description for brevity. ## INIT ### Next action +`initAction` + +Check that replica allocation is enabled from cluster settings (`cluster.routing.allocation.enabled`). Migrations will fail when replica allocation is disabled during the bulk index operation that waits for all active shards. Migrations wait for all active shards to ensure that saved objects are replicated to protect against data loss. + +The Elasticsearch documentation mentions switching off replica allocation when restoring a cluster and this is a setting that might be overlooked when a restore is done. Migrations will fail early if replica allocation is incorrectly set to avoid adding a write block to the old index before running into a failure later. + +If replica allocation is set to 'all', the migration continues to fetch the saved object indices: + `fetchIndices` Fetch the saved object indices, mappings and aliases to find the source index @@ -140,17 +148,21 @@ and determine whether we’re migrating from a legacy index or a v1 migrations index. ### New control state -1. If `.kibana` and the version specific aliases both exists and are pointing +1. Two conditions have to be met before migrations begin: + 1. If replica allocation is set as a persistent or transient setting to "perimaries", "new_primaries" or "none" fail the migration. Without replica allocation enabled or not set to 'all', the migration will timeout when waiting for index yellow status before bulk indexing. The check only considers persistent and transient settings and does not take static configuration in `elasticsearch.yml` into account. If `cluster.routing.allocation.enable` is configured in `elaticsearch.yml` and not set to the default of 'all', the migration will timeout. Static settings can only be returned from the `nodes/info` API. + → `FATAL` + + 2. If `.kibana` is pointing to an index that belongs to a later version of + Kibana .e.g. a 7.11.0 instance found the `.kibana` alias pointing to + `.kibana_7.12.0_001` fail the migration + → `FATAL` + +2. If `.kibana` and the version specific aliases both exists and are pointing to the same index. This version's migration has already been completed. Since the same version could have plugins enabled at any time that would introduce new transforms or mappings. → `OUTDATED_DOCUMENTS_SEARCH` -2. If `.kibana` is pointing to an index that belongs to a later version of -Kibana .e.g. a 7.11.0 instance found the `.kibana` alias pointing to -`.kibana_7.12.0_001` fail the migration - → `FATAL` - 3. If the `.kibana` alias exists we’re migrating from either a v1 or v2 index and the migration source index is the index the `.kibana` alias points to. → `WAIT_FOR_YELLOW_SOURCE` diff --git a/src/core/server/saved_objects/migrations/actions/index.ts b/src/core/server/saved_objects/migrations/actions/index.ts index 4e88e9c448d40..1123588309deb 100644 --- a/src/core/server/saved_objects/migrations/actions/index.ts +++ b/src/core/server/saved_objects/migrations/actions/index.ts @@ -20,6 +20,9 @@ export { export type { RetryableEsClientError }; // actions/* imports +export type { InitActionParams, UnsupportedClusterRoutingAllocation } from './initialize_action'; +export { initAction } from './initialize_action'; + export type { FetchIndexResponse, FetchIndicesParams } from './fetch_indices'; export { fetchIndices } from './fetch_indices'; @@ -81,6 +84,8 @@ export type { export { updateAndPickupMappings } from './update_and_pickup_mappings'; import type { UnknownDocsFound } from './check_for_unknown_docs'; +import type { UnsupportedClusterRoutingAllocation } from './initialize_action'; + export type { CheckForUnknownDocsParams, UnknownDocsFound, @@ -143,6 +148,7 @@ export interface ActionErrorTypeMap { documents_transform_failed: DocumentsTransformFailed; request_entity_too_large_exception: RequestEntityTooLargeException; unknown_docs_found: UnknownDocsFound; + unsupported_cluster_routing_allocation: UnsupportedClusterRoutingAllocation; } /** diff --git a/src/core/server/saved_objects/migrations/actions/initialize_action.test.ts b/src/core/server/saved_objects/migrations/actions/initialize_action.test.ts new file mode 100644 index 0000000000000..7c75470b890aa --- /dev/null +++ b/src/core/server/saved_objects/migrations/actions/initialize_action.test.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { catchRetryableEsClientErrors } from './catch_retryable_es_client_errors'; +import { errors as EsErrors } from '@elastic/elasticsearch'; +jest.mock('./catch_retryable_es_client_errors'); +import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks'; +import { initAction } from './initialize_action'; + +describe('initAction', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + const retryableError = new EsErrors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 503, + body: { error: { type: 'es_type', reason: 'es_reason' } }, + }) + ); + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) + ); + it('calls catchRetryableEsClientErrors when the promise rejects', async () => { + const task = initAction({ client, indices: ['my_index'] }); + try { + await task(); + } catch (e) { + /** ignore */ + } + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); + }); +}); diff --git a/src/core/server/saved_objects/migrations/actions/initialize_action.ts b/src/core/server/saved_objects/migrations/actions/initialize_action.ts new file mode 100644 index 0000000000000..73502382c9ca0 --- /dev/null +++ b/src/core/server/saved_objects/migrations/actions/initialize_action.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as TaskEither from 'fp-ts/lib/TaskEither'; +import * as Either from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { ElasticsearchClient } from '../../../elasticsearch'; +import { + catchRetryableEsClientErrors, + RetryableEsClientError, +} from './catch_retryable_es_client_errors'; + +import { FetchIndexResponse, fetchIndices } from './fetch_indices'; + +const routingAllocationEnable = 'cluster.routing.allocation.enable'; +export interface ClusterRoutingAllocationEnabled { + clusterRoutingAllocationEnabled: boolean; +} + +export interface InitActionParams { + client: ElasticsearchClient; + indices: string[]; +} + +export interface UnsupportedClusterRoutingAllocation { + type: 'unsupported_cluster_routing_allocation'; +} + +export const checkClusterRoutingAllocationEnabledTask = + ({ + client, + }: { + client: ElasticsearchClient; + }): TaskEither.TaskEither => + () => { + return client.cluster + .getSettings({ + flat_settings: true, + }) + .then((settings) => { + const clusterRoutingAllocations: string[] = + settings?.transient?.[routingAllocationEnable] ?? + settings?.persistent?.[routingAllocationEnable] ?? + []; + + const clusterRoutingAllocationEnabled = + [...clusterRoutingAllocations].length === 0 || + [...clusterRoutingAllocations].every((s: string) => s === 'all'); // if set, only allow 'all' + + if (!clusterRoutingAllocationEnabled) { + return Either.left({ type: 'unsupported_cluster_routing_allocation' as const }); + } else { + return Either.right({}); + } + }) + .catch(catchRetryableEsClientErrors); + }; + +export const initAction = ({ + client, + indices, +}: InitActionParams): TaskEither.TaskEither< + RetryableEsClientError | UnsupportedClusterRoutingAllocation, + FetchIndexResponse +> => { + return pipe( + checkClusterRoutingAllocationEnabledTask({ client }), + TaskEither.chainW((value) => { + return fetchIndices({ client, indices }); + }) + ); +}; diff --git a/src/core/server/saved_objects/migrations/actions/integration_tests/actions.test.ts b/src/core/server/saved_objects/migrations/actions/integration_tests/actions.test.ts index ef84f0cb49231..bac8f491534f0 100644 --- a/src/core/server/saved_objects/migrations/actions/integration_tests/actions.test.ts +++ b/src/core/server/saved_objects/migrations/actions/integration_tests/actions.test.ts @@ -14,7 +14,6 @@ import { cloneIndex, closePit, createIndex, - fetchIndices, openPit, OpenPitResponse, reindex, @@ -35,6 +34,7 @@ import { removeWriteBlock, transformDocs, waitForIndexStatusYellow, + initAction, } from '../../actions'; import * as Either from 'fp-ts/lib/Either'; import * as Option from 'fp-ts/lib/Option'; @@ -111,10 +111,20 @@ describe('migration actions', () => { await esServer.stop(); }); - describe('fetchIndices', () => { + describe('initAction', () => { + afterAll(async () => { + await client.cluster.putSettings({ + body: { + persistent: { + // Remove persistent test settings + cluster: { routing: { allocation: { enable: null } } }, + }, + }, + }); + }); it('resolves right empty record if no indices were found', async () => { expect.assertions(1); - const task = fetchIndices({ client, indices: ['no_such_index'] }); + const task = initAction({ client, indices: ['no_such_index'] }); await expect(task()).resolves.toMatchInlineSnapshot(` Object { "_tag": "Right", @@ -124,7 +134,7 @@ describe('migration actions', () => { }); it('resolves right record with found indices', async () => { expect.assertions(1); - const res = (await fetchIndices({ + const res = (await initAction({ client, indices: ['no_such_index', 'existing_index_with_docs'], })()) as Either.Right; @@ -139,6 +149,69 @@ describe('migration actions', () => { }) ); }); + it('resolves left with cluster routing allocation disabled', async () => { + expect.assertions(3); + await client.cluster.putSettings({ + body: { + persistent: { + // Disable all routing allocation + cluster: { routing: { allocation: { enable: 'none' } } }, + }, + }, + }); + const task = initAction({ + client, + indices: ['existing_index_with_docs'], + }); + await expect(task()).resolves.toMatchInlineSnapshot(` + Object { + "_tag": "Left", + "left": Object { + "type": "unsupported_cluster_routing_allocation", + }, + } + `); + await client.cluster.putSettings({ + body: { + persistent: { + // Allow routing to existing primaries only + cluster: { routing: { allocation: { enable: 'primaries' } } }, + }, + }, + }); + const task2 = initAction({ + client, + indices: ['existing_index_with_docs'], + }); + await expect(task2()).resolves.toMatchInlineSnapshot(` + Object { + "_tag": "Left", + "left": Object { + "type": "unsupported_cluster_routing_allocation", + }, + } + `); + await client.cluster.putSettings({ + body: { + persistent: { + // Allow routing to new primaries only + cluster: { routing: { allocation: { enable: 'new_primaries' } } }, + }, + }, + }); + const task3 = initAction({ + client, + indices: ['existing_index_with_docs'], + }); + await expect(task3()).resolves.toMatchInlineSnapshot(` + Object { + "_tag": "Left", + "left": Object { + "type": "unsupported_cluster_routing_allocation", + }, + } + `); + }); }); describe('setWriteBlock', () => { diff --git a/src/core/server/saved_objects/migrations/integration_tests/cluster_routing_allocation_disabled.test.ts b/src/core/server/saved_objects/migrations/integration_tests/cluster_routing_allocation_disabled.test.ts new file mode 100644 index 0000000000000..0f4522b156fe7 --- /dev/null +++ b/src/core/server/saved_objects/migrations/integration_tests/cluster_routing_allocation_disabled.test.ts @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import JSON5 from 'json5'; +import * as kbnTestServer from '../../../../test_helpers/kbn_server'; +import { Root } from '../../../root'; +import { ElasticsearchClient } from '../../../elasticsearch'; +import { LogRecord } from '@kbn/logging'; +import { retryAsync } from '../test_helpers/retry_async'; + +const logFilePath = Path.join(__dirname, 'unsupported_cluster_routing_allocation.log'); + +async function removeLogFile() { + // ignore errors if it doesn't exist + await fs.unlink(logFilePath).catch(() => void 0); +} + +const { startES } = kbnTestServer.createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + dataArchive: Path.join(__dirname, 'archives', '7.7.2_xpack_100k_obj.zip'), + }, + }, +}); + +function createKbnRoot() { + return kbnTestServer.createRootWithCorePlugins( + { + migrations: { + skip: false, + }, + logging: { + appenders: { + file: { + type: 'file', + fileName: logFilePath, + layout: { + type: 'json', + }, + }, + }, + loggers: [ + { + name: 'root', + level: 'info', + appenders: ['file'], + }, + ], + }, + }, + { + oss: false, + } + ); +} +const getClusterRoutingAllocations = (settings: Record) => { + const routingAllocations = + settings?.transient?.['cluster.routing.allocation.enable'] ?? + settings?.persistent?.['cluster.routing.allocation.enable'] ?? + []; + return ( + [...routingAllocations].length === 0 || + [...routingAllocations].every((s: string) => s === 'all') + ); // if set, only allow 'all'; +}; +let esServer: kbnTestServer.TestElasticsearchUtils; + +async function updateRoutingAllocations( + esClient: ElasticsearchClient, + settingType: string = 'persistent', + value: string = 'none' +) { + return await esClient.cluster.putSettings({ + [settingType]: { cluster: { routing: { allocation: { enable: value } } } }, + }); +} + +describe('unsupported_cluster_routing_allocation', () => { + let client: ElasticsearchClient; + let root: Root; + + beforeAll(async () => { + await removeLogFile(); + esServer = await startES(); + client = esServer.es.getClient(); + }); + afterAll(async () => { + await esServer.stop(); + }); + + it('fails with a descriptive message when persistent replica allocation is not enabled', async () => { + const initialSettings = await client.cluster.getSettings({ flat_settings: true }); + + expect(getClusterRoutingAllocations(initialSettings)).toBe(true); + + await updateRoutingAllocations(client, 'persistent', 'none'); + + const updatedSettings = await client.cluster.getSettings({ flat_settings: true }); + + expect(getClusterRoutingAllocations(updatedSettings)).toBe(false); + + // now try to start Kibana + root = createKbnRoot(); + await root.preboot(); + await root.setup(); + + await expect(root.start()).rejects.toMatchInlineSnapshot( + `[Error: Unable to complete saved object migrations for the [.kibana] index: The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue. To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {"transient": {"cluster.routing.allocation.enable": null}, "persistent": {"cluster.routing.allocation.enable": null}}]` + ); + + await retryAsync( + async () => { + const logFileContent = await fs.readFile(logFilePath, 'utf-8'); + const records = logFileContent + .split('\n') + .filter(Boolean) + .map((str) => JSON5.parse(str)) as LogRecord[]; + expect( + records.find((rec) => + rec.message.startsWith( + `Unable to complete saved object migrations for the [.kibana] index: The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue.` + ) + ) + ).toBeDefined(); + }, + { retryAttempts: 10, retryDelayMs: 200 } + ); + }); + + it('fails with a descriptive message when persistent replica allocation is set to "primaries"', async () => { + await updateRoutingAllocations(client, 'persistent', 'primaries'); + + const updatedSettings = await client.cluster.getSettings({ flat_settings: true }); + + expect(getClusterRoutingAllocations(updatedSettings)).toBe(false); + + // now try to start Kibana + root = createKbnRoot(); + await root.preboot(); + await root.setup(); + + await expect(root.start()).rejects.toMatchInlineSnapshot( + `[Error: Unable to complete saved object migrations for the [.kibana] index: The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue. To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {"transient": {"cluster.routing.allocation.enable": null}, "persistent": {"cluster.routing.allocation.enable": null}}]` + ); + }); +}); diff --git a/src/core/server/saved_objects/migrations/kibana_migrator.test.ts b/src/core/server/saved_objects/migrations/kibana_migrator.test.ts index 4bb24a3f8240d..2adf4d5dee184 100644 --- a/src/core/server/saved_objects/migrations/kibana_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/kibana_migrator.test.ts @@ -110,10 +110,16 @@ describe('KibanaMigrator', () => { it('only runs migrations once if called multiple times', async () => { const options = mockOptions(); - options.client.indices.get.mockResponse({}, { statusCode: 404 }); options.client.indices.getAlias.mockResponse({}, { statusCode: 404 }); + options.client.cluster.getSettings.mockResponse( + { + transient: {}, + persistent: {}, + }, + { statusCode: 404 } + ); const migrator = new KibanaMigrator(options); migrator.prepareMigrations(); @@ -197,6 +203,13 @@ type MockedOptions = KibanaMigratorOptions & { const mockV2MigrationOptions = () => { const options = mockOptions(); + options.client.cluster.getSettings.mockResponse( + { + transient: {}, + persistent: {}, + }, + { statusCode: 200 } + ); options.client.indices.get.mockResponse( { diff --git a/src/core/server/saved_objects/migrations/model/model.test.ts b/src/core/server/saved_objects/migrations/model/model.test.ts index 5ca6713ca163f..de8483bb4abce 100644 --- a/src/core/server/saved_objects/migrations/model/model.test.ts +++ b/src/core/server/saved_objects/migrations/model/model.test.ts @@ -291,6 +291,17 @@ describe('migrations v2 model', () => { expect(newState.retryCount).toEqual(0); expect(newState.retryDelay).toEqual(0); }); + test('INIT -> FATAL when cluster routing allocation is not enabled', () => { + const res: ResponseType<'INIT'> = Either.left({ + type: 'unsupported_cluster_routing_allocation', + }); + const newState = model(initState, res) as FatalState; + + expect(newState.controlState).toEqual('FATAL'); + expect(newState.reason).toMatchInlineSnapshot( + `"The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue. To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {\\"transient\\": {\\"cluster.routing.allocation.enable\\": null}, \\"persistent\\": {\\"cluster.routing.allocation.enable\\": null}}"` + ); + }); test("INIT -> FATAL when .kibana points to newer version's index", () => { const res: ResponseType<'INIT'> = Either.right({ '.kibana_7.12.0_001': { diff --git a/src/core/server/saved_objects/migrations/model/model.ts b/src/core/server/saved_objects/migrations/model/model.ts index e9efb72bca6f5..c2f11ba18069c 100644 --- a/src/core/server/saved_objects/migrations/model/model.ts +++ b/src/core/server/saved_objects/migrations/model/model.ts @@ -72,7 +72,26 @@ export const model = (currentState: State, resW: ResponseType): if (stateP.controlState === 'INIT') { const res = resW as ExcludeRetryableEsError>; - if (Either.isRight(res)) { + if (Either.isLeft(res)) { + const left = res.left; + if (isLeftTypeof(left, 'unsupported_cluster_routing_allocation')) { + return { + ...stateP, + controlState: 'FATAL', + reason: `The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue. To proceed, please remove the cluster routing allocation settings with PUT /_cluster/settings {"transient": {"cluster.routing.allocation.enable": null}, "persistent": {"cluster.routing.allocation.enable": null}}`, + logs: [ + ...stateP.logs, + { + level: 'error', + message: `The elasticsearch cluster has cluster routing allocation incorrectly set for migrations to continue. Ensure that the persistent and transient Elasticsearch configuration option 'cluster.routing.allocation.enable' is not set or set it to a value of 'all'.`, + }, + ], + }; + } else { + return throwBadResponse(stateP, left); + } + } else if (Either.isRight(res)) { + // cluster routing allocation is enabled and we can continue with the migration as normal const indices = res.right; const aliases = getAliases(indices); diff --git a/src/core/server/saved_objects/migrations/next.ts b/src/core/server/saved_objects/migrations/next.ts index 419b350a0b5f6..24a4204c3009e 100644 --- a/src/core/server/saved_objects/migrations/next.ts +++ b/src/core/server/saved_objects/migrations/next.ts @@ -59,7 +59,7 @@ export type ResponseType = Awaited< export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: TransformRawDocs) => { return { INIT: (state: InitState) => - Actions.fetchIndices({ client, indices: [state.currentAlias, state.versionAlias] }), + Actions.initAction({ client, indices: [state.currentAlias, state.versionAlias] }), WAIT_FOR_YELLOW_SOURCE: (state: WaitForYellowSourceState) => Actions.waitForIndexStatusYellow({ client, index: state.sourceIndex.value }), CHECK_UNKNOWN_DOCUMENTS: (state: CheckUnknownDocumentsState) => From c4f8fc5995b6294212d7c2e25c12646adc9151c4 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Tue, 8 Mar 2022 11:00:53 -0600 Subject: [PATCH 069/140] [shared-ux] CODEOWNERS update (#127169) Co-authored-by: Spencer --- .github/CODEOWNERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0a0aa994fb70b..aada1dd1127e4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -72,8 +72,6 @@ /src/plugins/field_formats/ @elastic/kibana-app-services /src/plugins/data_view_editor/ @elastic/kibana-app-services /src/plugins/inspector/ @elastic/kibana-app-services -/src/plugins/kibana_react/ @elastic/kibana-app-services -/src/plugins/kibana_react/public/code_editor @elastic/kibana-presentation /src/plugins/kibana_utils/ @elastic/kibana-app-services /src/plugins/navigation/ @elastic/kibana-app-services /src/plugins/share/ @elastic/kibana-app-services @@ -467,9 +465,11 @@ x-pack/plugins/session_view @elastic/awp-platform #CC# /x-pack/plugins/reporting/ @elastic/kibana-reporting-services # EUI design -/src/plugins/kibana_react/public/page_template/ @elastic/eui-design @elastic/kibana-app-services +/src/plugins/kibana_react/public/page_template/ @elastic/eui-design @elastic/shared-ux # Application Experience ## Shared UX -/src/plugins/shared_ux @elastic/shared-ux +/src/plugins/shared_ux/ @elastic/shared-ux +/src/plugins/kibana_react/ @elastic/shared-ux +/src/plugins/kibana_react/public/code_editor @elastic/kibana-presentation From 574a38fcd527724658fc1080e811490351219147 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Tue, 8 Mar 2022 18:28:42 +0100 Subject: [PATCH 070/140] [Security Solution] Fixes and unskips export rule test (#127060) * fixes and unskips export rule test * fixes failing tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../detection_rules/export_rule.spec.ts | 15 ++++++----- .../detection_rules/import_rules.spec.ts | 25 ++++++++----------- .../cypress/tasks/alerts_detection_rules.ts | 19 -------------- 3 files changed, 20 insertions(+), 39 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts index 1e1abaa326bd4..7b84845d46323 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts @@ -6,15 +6,17 @@ */ import { expectedExportedRule, getNewRule } from '../../objects/rule'; -import { exportFirstRule, getRulesImportExportToast } from '../../tasks/alerts_detection_rules'; + +import { TOASTER } from '../../screens/alerts_detection_rules'; + +import { exportFirstRule } from '../../tasks/alerts_detection_rules'; import { createCustomRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; -// Flaky https://github.com/elastic/kibana/issues/69849 -describe.skip('Export rules', () => { +describe('Export rules', () => { beforeEach(() => { cleanKibana(); cy.intercept( @@ -29,9 +31,10 @@ describe.skip('Export rules', () => { exportFirstRule(); cy.wait('@export').then(({ response }) => { cy.wrap(response?.body).should('eql', expectedExportedRule(this.ruleResponse)); - getRulesImportExportToast([ - 'Successfully exported 1 of 1 rule. Prebuilt rules were excluded from the resulting file.', - ]); + cy.get(TOASTER).should( + 'have.text', + 'Successfully exported 1 of 1 rule. Prebuilt rules were excluded from the resulting file.' + ); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/import_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/import_rules.spec.ts index c82d2c680525f..5a2a58e89c3d3 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/import_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/import_rules.spec.ts @@ -5,11 +5,8 @@ * 2.0. */ -import { - getRulesImportExportToast, - importRules, - importRulesWithOverwriteAll, -} from '../../tasks/alerts_detection_rules'; +import { TOASTER } from '../../screens/alerts_detection_rules'; +import { importRules, importRulesWithOverwriteAll } from '../../tasks/alerts_detection_rules'; import { cleanKibana, reload } from '../../tasks/common'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; @@ -27,10 +24,10 @@ describe('Import rules', () => { cy.wait('@import').then(({ response }) => { cy.wrap(response?.statusCode).should('eql', 200); - getRulesImportExportToast([ - 'Successfully imported 1 rule', - 'Successfully imported 2 exceptions.', - ]); + cy.get(TOASTER).should( + 'have.text', + 'Successfully imported 1 ruleSuccessfully imported 2 exceptions.' + ); }); }); @@ -46,7 +43,7 @@ describe('Import rules', () => { cy.wait('@import').then(({ response }) => { cy.wrap(response?.statusCode).should('eql', 200); - getRulesImportExportToast(['Failed to import 1 rule', 'Failed to import 2 exceptions']); + cy.get(TOASTER).should('have.text', 'Failed to import 1 ruleFailed to import 2 exceptions'); }); }); @@ -62,10 +59,10 @@ describe('Import rules', () => { cy.wait('@import').then(({ response }) => { cy.wrap(response?.statusCode).should('eql', 200); - getRulesImportExportToast([ - 'Successfully imported 1 rule', - 'Successfully imported 2 exceptions.', - ]); + cy.get(TOASTER).should( + 'have.text', + 'Successfully imported 1 ruleSuccessfully imported 2 exceptions.' + ); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index ab09aca83f575..8d125c242be35 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -42,7 +42,6 @@ import { RULE_IMPORT_MODAL_BUTTON, RULE_IMPORT_MODAL, INPUT_FILE, - TOASTER, RULE_IMPORT_OVERWRITE_CHECKBOX, RULE_IMPORT_OVERWRITE_EXCEPTIONS_CHECKBOX, RULES_TAGS_POPOVER_BTN, @@ -253,24 +252,6 @@ export const importRules = (rulesFile: string) => { cy.get(INPUT_FILE).should('not.exist'); }; -export const getRulesImportExportToast = (headers: string[]) => { - cy.get(TOASTER) - .should('exist') - .then(($els) => { - const arrayOfText = Cypress.$.makeArray($els).map((el) => el.innerText); - - return headers.reduce((areAllIncluded, header) => { - const isContained = arrayOfText.includes(header); - if (!areAllIncluded) { - return false; - } else { - return isContained; - } - }, true); - }) - .should('be.true'); -}; - export const selectOverwriteRulesImport = () => { cy.get(RULE_IMPORT_OVERWRITE_CHECKBOX) .pipe(($el) => $el.trigger('click')) From b5e194532d58ce0d3193413a647c56fb36d5447c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ece=20=C3=96zalp?= Date: Tue, 8 Mar 2022 13:18:26 -0500 Subject: [PATCH 071/140] [CTI] Enable IM Rule Preview (#126651) --- .../schemas/request/rule_schemas.ts | 2 + .../components/rules/rule_preview/index.tsx | 3 +- .../rules/rule_preview/preview_logs.tsx | 56 ++++++++++++++----- .../rules/rule_preview/translations.ts | 7 +++ .../rules/rule_preview/use_preview_route.tsx | 4 ++ .../rules/step_define_rule/index.tsx | 49 ++++++++-------- .../rules/use_preview_rule.ts | 1 + .../routes/rules/preview_rules_route.ts | 43 +++++++++----- .../preview/preview_rule_execution_logger.ts | 24 ++------ .../tests/preview_rules.ts | 2 +- 10 files changed, 115 insertions(+), 76 deletions(-) diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts index 78ac7d6f1bbef..04d482c847138 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts @@ -428,9 +428,11 @@ export interface RulePreviewLogs { errors: string[]; warnings: string[]; startedAt?: string; + duration: number; } export interface PreviewResponse { previewId: string | undefined; logs: RulePreviewLogs[] | undefined; + isAborted: boolean | undefined; } diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx index 05dd0ff12c559..2dac0b68b0299 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx @@ -92,6 +92,7 @@ const RulePreviewComponent: React.FC = ({ previewId, logs, hasNoiseWarning, + isAborted, } = usePreviewRoute({ index, isDisabled, @@ -159,7 +160,7 @@ const RulePreviewComponent: React.FC = ({ index={index} /> )} - + ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_logs.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_logs.tsx index 726c2b5df964c..45fa4f2e20aff 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_logs.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_logs.tsx @@ -13,9 +13,11 @@ import * as i18n from './translations'; interface PreviewLogsComponentProps { logs: RulePreviewLogs[]; hasNoiseWarning: boolean; + isAborted: boolean; } interface SortedLogs { + duration: number; startedAt?: string; logs: string[]; } @@ -25,12 +27,25 @@ interface LogAccordionProps { isError?: boolean; } -const addLogs = (startedAt: string | undefined, logs: string[], allLogs: SortedLogs[]) => - logs.length ? [{ startedAt, logs }, ...allLogs] : allLogs; +const CustomWarning: React.FC<{ message: string }> = ({ message }) => ( + + +

{message}

+
+
+); + +const addLogs = ( + startedAt: string | undefined, + logs: string[], + duration: number, + allLogs: SortedLogs[] +) => (logs.length ? [{ startedAt, logs, duration }, ...allLogs] : allLogs); export const PreviewLogsComponent: React.FC = ({ logs, hasNoiseWarning, + isAborted, }) => { const sortedLogs = useMemo( () => @@ -39,8 +54,8 @@ export const PreviewLogsComponent: React.FC = ({ warnings: SortedLogs[]; }>( ({ errors, warnings }, curr) => ({ - errors: addLogs(curr.startedAt, curr.errors, errors), - warnings: addLogs(curr.startedAt, curr.warnings, warnings), + errors: addLogs(curr.startedAt, curr.errors, curr.duration, errors), + warnings: addLogs(curr.startedAt, curr.warnings, curr.duration, warnings), }), { errors: [], warnings: [] } ), @@ -49,19 +64,32 @@ export const PreviewLogsComponent: React.FC = ({ return ( <> - {hasNoiseWarning ?? } + {hasNoiseWarning ?? } - + + {isAborted && } + ); }; -const LogAccordion: React.FC = ({ logs, isError }) => { +const LogAccordion: React.FC = ({ logs, isError, children }) => { const firstLog = logs[0]; - const restOfLogs = logs.slice(1); - return firstLog ? ( + if (!(children || firstLog)) return null; + + const restOfLogs = children ? logs : logs.slice(1); + const bannerElement = children ?? ( + + ); + + return ( <> - + {bannerElement} {restOfLogs.length > 0 ? ( = ({ logs, isError }) => { key={`accordion-log-${key}`} logs={log.logs} startedAt={log.startedAt} + duration={log.duration} isError={isError} /> ))} @@ -81,14 +110,15 @@ const LogAccordion: React.FC = ({ logs, isError }) => { ) : null} - ) : null; + ); }; export const CalloutGroup: React.FC<{ logs: string[]; + duration: number; startedAt?: string; isError?: boolean; -}> = ({ logs, startedAt, isError }) => { +}> = ({ logs, startedAt, isError, duration }) => { return logs.length > 0 ? ( <> {logs.map((log, i) => ( @@ -97,7 +127,7 @@ export const CalloutGroup: React.FC<{ color={isError ? 'danger' : 'warning'} iconType="alert" data-test-subj={isError ? 'preview-error' : 'preview-warning'} - title={startedAt != null ? `[${startedAt}]` : null} + title={`${startedAt ? `[${startedAt}] ` : ''}[${duration}ms]`} >

{log}

diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/translations.ts index 23fcf62f32a6c..58a90fba13dc9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/translations.ts @@ -30,6 +30,13 @@ export const QUERY_PREVIEW_BUTTON = i18n.translate( } ); +export const PREVIEW_TIMEOUT_WARNING = i18n.translate( + 'xpack.securitySolution.stepDefineRule.previewTimeoutWarning', + { + defaultMessage: 'Preview timed out after 60 seconds', + } +); + export const QUERY_PREVIEW_SELECT_ARIA = i18n.translate( 'xpack.securitySolution.stepDefineRule.previewQueryAriaLabel', { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx index 2e51090f37a94..d5278144cf70a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx @@ -45,10 +45,12 @@ export const usePreviewRoute = ({ const { isLoading, response, rule, setRule } = usePreviewRule(timeFrame); const [logs, setLogs] = useState(response.logs ?? []); + const [isAborted, setIsAborted] = useState(!!response.isAborted); const [hasNoiseWarning, setHasNoiseWarning] = useState(false); useEffect(() => { setLogs(response.logs ?? []); + setIsAborted(!!response.isAborted); }, [response]); const addNoiseWarning = useCallback(() => { @@ -58,6 +60,7 @@ export const usePreviewRoute = ({ const clearPreview = useCallback(() => { setRule(null); setLogs([]); + setIsAborted(false); setIsRequestTriggered(false); setHasNoiseWarning(false); }, [setRule]); @@ -120,5 +123,6 @@ export const usePreviewRoute = ({ isPreviewRequestInProgress: isLoading, previewId: response.previewId ?? '', logs, + isAborted, }; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index 69d4d400f12a4..2f1d214f04573 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -6,7 +6,7 @@ */ import { EuiButtonEmpty, EuiFormRow, EuiSpacer } from '@elastic/eui'; -import React, { FC, memo, useCallback, useState, useEffect, useMemo } from 'react'; +import React, { FC, memo, useCallback, useState, useEffect } from 'react'; import styled from 'styled-components'; import { isEqual } from 'lodash'; @@ -190,7 +190,6 @@ const StepDefineRuleComponent: FC = ({ const machineLearningJobId = formMachineLearningJobId ?? initialState.machineLearningJobId; const anomalyThreshold = formAnomalyThreshold ?? initialState.anomalyThreshold; const ruleType = formRuleType || initialState.ruleType; - const isPreviewRouteEnabled = useMemo(() => ruleType !== 'threat_match', [ruleType]); const [indexPatternsLoading, { browserFields, indexPatterns }] = useFetchIndex(index); const aggregatableFields = Object.entries(browserFields).reduce( (groupAcc, [groupName, groupValue]) => { @@ -504,31 +503,27 @@ const StepDefineRuleComponent: FC = ({ }} /> - {isPreviewRouteEnabled && ( - <> - - - - )} + + {!isUpdateView && ( diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts index 4b0ad0507263d..43572ddaf4d37 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_preview_rule.ts @@ -22,6 +22,7 @@ import { transformOutput } from './transforms'; const emptyPreviewRule: PreviewResponse = { previewId: undefined, logs: [], + isAborted: false, }; export const usePreviewRule = (timeframe: Unit = 'h') => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts index 95dcb918cd165..8b063e4f1b65c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -47,6 +47,9 @@ import { } from '../../rule_types'; import { createSecurityRuleTypeWrapper } from '../../rule_types/create_security_rule_type_wrapper'; import { RULE_PREVIEW_INVOCATION_COUNT } from '../../../../../common/detection_engine/constants'; +import { RuleExecutionContext, StatusChangeArgs } from '../../rule_execution_log'; + +const PREVIEW_TIMEOUT_SECONDS = 60; export const previewRulesRoute = async ( router: SecuritySolutionPluginRouter, @@ -87,13 +90,7 @@ export const previewRulesRoute = async ( ].includes(invocationCount) ) { return response.ok({ - body: { logs: [{ errors: ['Invalid invocation count'], warnings: [] }] }, - }); - } - - if (request.body.type === 'threat_match') { - return response.ok({ - body: { logs: [{ errors: ['Preview for rule type not supported'], warnings: [] }] }, + body: { logs: [{ errors: ['Invalid invocation count'], warnings: [], duration: 0 }] }, }); } @@ -112,9 +109,11 @@ export const previewRulesRoute = async ( const spaceId = siemClient.getSpaceId(); const previewId = uuid.v4(); const username = security?.authc.getCurrentUser(request)?.username; - const previewRuleExecutionLogger = createPreviewRuleExecutionLogger(); + const loggedStatusChanges: Array = []; + const previewRuleExecutionLogger = createPreviewRuleExecutionLogger(loggedStatusChanges); const runState: Record = {}; const logs: RulePreviewLogs[] = []; + let isAborted = false; const previewRuleTypeWrapper = createSecurityRuleTypeWrapper({ ...securityRuleTypeOptions, @@ -158,6 +157,12 @@ export const previewRulesRoute = async ( ) => { let statePreview = runState as TState; + const abortController = new AbortController(); + setTimeout(() => { + abortController.abort(); + isAborted = true; + }, PREVIEW_TIMEOUT_SECONDS * 1000); + const startedAt = moment(); const parsedDuration = parseDuration(internalRule.schedule.interval) ?? 0; startedAt.subtract(moment.duration(parsedDuration * (invocationCount - 1))); @@ -175,7 +180,11 @@ export const previewRulesRoute = async ( updatedBy: username ?? 'preview-updated-by', }; - while (invocationCount > 0) { + let invocationStartTime; + + while (invocationCount > 0 && !isAborted) { + invocationStartTime = moment(); + statePreview = (await executor({ alertId: previewId, createdBy: rule.createdBy, @@ -188,10 +197,9 @@ export const previewRulesRoute = async ( shouldWriteAlerts, shouldStopExecution: () => false, alertFactory, - // Just use es client always for preview search: createAbortableEsClientFactory({ scopedClusterClient: context.core.elasticsearch.client, - abortController: new AbortController(), + abortController, }), savedObjectsClient: context.core.savedObjects.client, scopedClusterClient: context.core.elasticsearch.client, @@ -204,12 +212,11 @@ export const previewRulesRoute = async ( updatedBy: rule.updatedBy, })) as TState; - // Save and reset error and warning logs - const errors = previewRuleExecutionLogger.logged.statusChanges + const errors = loggedStatusChanges .filter((item) => item.newStatus === RuleExecutionStatus.failed) .map((item) => item.message ?? 'Unkown Error'); - const warnings = previewRuleExecutionLogger.logged.statusChanges + const warnings = loggedStatusChanges .filter((item) => item.newStatus === RuleExecutionStatus['partial failure']) .map((item) => item.message ?? 'Unknown Warning'); @@ -217,9 +224,14 @@ export const previewRulesRoute = async ( errors, warnings, startedAt: startedAt.toDate().toISOString(), + duration: moment().diff(invocationStartTime, 'milliseconds'), }); - previewRuleExecutionLogger.clearLogs(); + loggedStatusChanges.length = 0; + + if (errors.length) { + break; + } previousStartedAt = startedAt.toDate(); startedAt.add(parseInterval(internalRule.schedule.interval)); @@ -301,6 +313,7 @@ export const previewRulesRoute = async ( body: { previewId, logs, + isAborted, }, }); } catch (err) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_logger.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_logger.ts index afa24e133b58e..55ce5913e5903 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_logger.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_logger.ts @@ -13,19 +13,11 @@ import { export interface IPreviewRuleExecutionLogger { factory: RuleExecutionLogForExecutorsFactory; - - logged: { - statusChanges: Array; - }; - - clearLogs(): void; } -export const createPreviewRuleExecutionLogger = () => { - let logged: IPreviewRuleExecutionLogger['logged'] = { - statusChanges: [], - }; - +export const createPreviewRuleExecutionLogger = ( + loggedStatusChanges: Array +) => { const factory: RuleExecutionLogForExecutorsFactory = ( savedObjectsClient, eventLogService, @@ -36,17 +28,11 @@ export const createPreviewRuleExecutionLogger = () => { context, logStatusChange(args: StatusChangeArgs): Promise { - logged.statusChanges.push({ ...context, ...args }); + loggedStatusChanges.push({ ...context, ...args }); return Promise.resolve(); }, }; }; - const clearLogs = (): void => { - logged = { - statusChanges: [], - }; - }; - - return { factory, logged, clearLogs }; + return { factory }; }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/preview_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/preview_rules.ts index 4832da8d0cfaa..14af40f901461 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/preview_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/preview_rules.ts @@ -65,7 +65,7 @@ export default ({ getService }: FtrProviderContext) => { .send(getSimplePreviewRule('', 3)) .expect(200); const { logs } = getSimpleRulePreviewOutput(undefined, [ - { errors: ['Invalid invocation count'], warnings: [] }, + { errors: ['Invalid invocation count'], warnings: [], duration: 0 }, ]); expect(body).to.eql({ logs }); }); From 2fa485a29bf0cfec21e344071fde0c5b5c55db6f Mon Sep 17 00:00:00 2001 From: mgiota Date: Tue, 8 Mar 2022 19:32:19 +0100 Subject: [PATCH 072/140] 123580 o11 rules page (#124132) * testing find rules api & load observability rules * add rules option in observability sidebar * make rules menu option configurable * create o11y rules page * update manage rules link * rules page * filter o11y rule types only(temp solution) * remove unused stuff * Add documentation link * fix typescript errors * add selection to EuiBasicTable * add rest columns in the table * toggle popover * add actions column * temp * add breadcrumbs to rules page * add pagination to rules page * add onChange handler in rules pagination * add create rule button * add icon type to the create rule button * Show number of rules * add auto refresh button * use correct rule management link in apm based on feature flag * use correct rule management link in infra and observability alerts page based on on feature flag * centralize useRulesLink logic inside the new useRulesLinkCreator observability hook * useRulesLink in uptime * useRulesLink in apm * mock observability useLinks function in uptime tests * temporarily remove the create rule button * remove unused console statement * remove unused button * fix uptime failing tests * remove useContextForPlugin and rename hook to create_use_rules_link * remove useKibanaForContext from uptime and use useKibana instead * remove unused imports * add a todo comment in the loadRules export * use await import for createUseRulesLink and declare core and setup as async * fix typescript error * Revert "fix typescript error" This reverts commit c5a67d5d56dc26bf6dd7ce242705a9a214f123be. * Revert "use await import for createUseRulesLink and declare core and setup as async" This reverts commit 627a6265cf2b28ff212a267b923b362a4d0b1952. * experiment with page bundle size * fix for useEffect * use async import for loadRules * experiments * Revert "experiments" This reverts commit 8b389dbf6a5bf34082f2fce9a4ede0072c529633. * increase page bundle size limit Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- packages/kbn-optimizer/limits.yml | 2 +- .../alerting_popover_flyout.tsx | 9 +- .../components/metrics_alert_dropdown.tsx | 9 +- .../manage_alerts_context_menu_item.tsx | 8 +- .../components/alert_dropdown.tsx | 4 +- .../public/application/application.test.tsx | 1 + .../components/app/section/apm/index.test.tsx | 1 + .../components/app/section/ux/index.test.tsx | 1 + .../public/hooks/create_use_rules_link.ts | 22 ++ .../public/hooks/use_rules_link.ts | 20 -- .../public/hooks/use_time_range.test.ts | 2 + x-pack/plugins/observability/public/index.ts | 3 +- .../containers/alerts_page/alerts_page.tsx | 9 +- .../pages/overview/overview.stories.tsx | 1 + .../public/pages/rules/index.tsx | 261 ++++++++++++++++++ x-pack/plugins/observability/public/plugin.ts | 14 +- .../observability/public/routes/index.tsx | 8 + .../public/update_global_navigation.tsx | 8 + .../public/utils/test_helper.tsx | 1 + x-pack/plugins/observability/server/index.ts | 1 + .../triggers_actions_ui/public/index.ts | 2 + .../alerts/toggle_alert_flyout_button.tsx | 14 +- .../uptime/public/lib/helper/rtl_helpers.tsx | 1 + 23 files changed, 357 insertions(+), 45 deletions(-) create mode 100644 x-pack/plugins/observability/public/hooks/create_use_rules_link.ts delete mode 100644 x-pack/plugins/observability/public/hooks/use_rules_link.ts create mode 100644 x-pack/plugins/observability/public/pages/rules/index.tsx diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index afe7fcd9ddc86..4df5d02e010db 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -41,7 +41,7 @@ pageLoadAssetSize: monitoring: 80000 navigation: 37269 newsfeed: 42228 - observability: 89709 + observability: 95000 painlessLab: 179748 remoteClusters: 51327 rollup: 97204 diff --git a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx index 4c593e80df0cb..f988917515fbb 100644 --- a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx +++ b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx @@ -16,6 +16,7 @@ import React, { useState } from 'react'; import { IBasePath } from '../../../../../../../src/core/public'; import { AlertType } from '../../../../common/alert_types'; import { AlertingFlyout } from '../../alerting/alerting_flyout'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; const alertLabel = i18n.translate('xpack.apm.home.alertsMenu.alerts', { defaultMessage: 'Alerts and rules', @@ -63,7 +64,9 @@ export function AlertingPopoverAndFlyout({ }: Props) { const [popoverOpen, setPopoverOpen] = useState(false); const [alertType, setAlertType] = useState(null); - + const { + plugins: { observability }, + } = useApmPluginContext(); const button = ( { const [popoverOpen, setPopoverOpen] = useState(false); const [visibleFlyoutType, setVisibleFlyoutType] = useState(null); const uiCapabilities = useKibana().services.application?.capabilities; - + const { + services: { observability }, + } = useKibana(); const canCreateAlerts = useMemo( () => Boolean(uiCapabilities?.infrastructure?.save), [uiCapabilities] @@ -83,7 +86,7 @@ export const MetricsAlertDropdown = () => { [setVisibleFlyoutType, closePopover] ); - const manageRulesLinkProps = useRulesLink(); + const manageRulesLinkProps = observability.useRulesLink(); const manageAlertsMenuItem = useMemo( () => ({ diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/manage_alerts_context_menu_item.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/manage_alerts_context_menu_item.tsx index 4bd0438af9b76..c35335afe493f 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/components/manage_alerts_context_menu_item.tsx +++ b/x-pack/plugins/infra/public/alerting/inventory/components/manage_alerts_context_menu_item.tsx @@ -8,10 +8,14 @@ import { EuiContextMenuItem } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useRulesLink } from '../../../../../observability/public'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { InfraClientStartDeps } from '../../../types'; export const ManageAlertsContextMenuItem = () => { - const manageRulesLinkProps = useRulesLink(); + const { + services: { observability }, + } = useKibana(); + const manageRulesLinkProps = observability.useRulesLink(); return ( diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx index 9fbc88ed5352f..a91e2d6ad92f6 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx @@ -10,7 +10,6 @@ import { i18n } from '@kbn/i18n'; import { EuiPopover, EuiContextMenuItem, EuiContextMenuPanel, EuiHeaderLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { AlertFlyout } from './alert_flyout'; -import { useRulesLink } from '../../../../../observability/public'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; const readOnlyUserTooltipContent = i18n.translate( @@ -31,13 +30,14 @@ export const AlertDropdown = () => { const { services: { application: { capabilities }, + observability, }, } = useKibanaContextForPlugin(); const canCreateAlerts = capabilities?.logs?.save ?? false; const [popoverOpen, setPopoverOpen] = useState(false); const [flyoutVisible, setFlyoutVisible] = useState(false); - const manageRulesLinkProps = useRulesLink({ + const manageRulesLinkProps = observability.useRulesLink({ hrefOnly: true, }); diff --git a/x-pack/plugins/observability/public/application/application.test.tsx b/x-pack/plugins/observability/public/application/application.test.tsx index d28667d147b29..e2250962c671c 100644 --- a/x-pack/plugins/observability/public/application/application.test.tsx +++ b/x-pack/plugins/observability/public/application/application.test.tsx @@ -58,6 +58,7 @@ describe('renderApp', () => { alertingExperience: { enabled: true }, cases: { enabled: true }, overviewNext: { enabled: false }, + rules: { enabled: false }, }, }; diff --git a/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx index 5e45eda0d3176..c5459633bd8eb 100644 --- a/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx @@ -47,6 +47,7 @@ describe('APMSection', () => { alertingExperience: { enabled: true }, cases: { enabled: true }, overviewNext: { enabled: false }, + rules: { enabled: false }, }, }, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx index d1597b9a94021..e464e545adad1 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx @@ -47,6 +47,7 @@ describe('UXSection', () => { alertingExperience: { enabled: true }, cases: { enabled: true }, overviewNext: { enabled: false }, + rules: { enabled: false }, }, }, plugins: { diff --git a/x-pack/plugins/observability/public/hooks/create_use_rules_link.ts b/x-pack/plugins/observability/public/hooks/create_use_rules_link.ts new file mode 100644 index 0000000000000..adf20c3a2b47d --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/create_use_rules_link.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { Options, useLinkProps } from './use_link_props'; + +export function createUseRulesLink(isNewRuleManagementEnabled = false) { + return function (options: Options = {}) { + const linkProps = isNewRuleManagementEnabled + ? { + app: 'observability', + pathname: '/rules', + } + : { + app: 'management', + pathname: '/insightsAndAlerting/triggersActions/alerts', + }; + return useLinkProps(linkProps, options); + }; +} diff --git a/x-pack/plugins/observability/public/hooks/use_rules_link.ts b/x-pack/plugins/observability/public/hooks/use_rules_link.ts deleted file mode 100644 index af9f1967d81fd..0000000000000 --- a/x-pack/plugins/observability/public/hooks/use_rules_link.ts +++ /dev/null @@ -1,20 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useLinkProps, Options, LinkProps } from './use_link_props'; - -export function useRulesLink(options?: Options): LinkProps { - const manageRulesLinkProps = useLinkProps( - { - app: 'management', - pathname: '/insightsAndAlerting/triggersActions/alerts', - }, - options - ); - - return manageRulesLinkProps; -} diff --git a/x-pack/plugins/observability/public/hooks/use_time_range.test.ts b/x-pack/plugins/observability/public/hooks/use_time_range.test.ts index bbf3096e55107..246aa42820b52 100644 --- a/x-pack/plugins/observability/public/hooks/use_time_range.test.ts +++ b/x-pack/plugins/observability/public/hooks/use_time_range.test.ts @@ -29,6 +29,7 @@ describe('useTimeRange', () => { alertingExperience: { enabled: true }, cases: { enabled: true }, overviewNext: { enabled: false }, + rules: { enabled: false }, }, }, plugins: { @@ -78,6 +79,7 @@ describe('useTimeRange', () => { alertingExperience: { enabled: true }, cases: { enabled: true }, overviewNext: { enabled: false }, + rules: { enabled: false }, }, }, plugins: { diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 0ef02fda3fa47..72b3b1b46f69e 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -34,6 +34,7 @@ export { uptimeOverviewLocatorID } from '../common'; export interface ConfigSchema { unsafe: { alertingExperience: { enabled: boolean }; + rules: { enabled: boolean }; cases: { enabled: boolean }; overviewNext: { enabled: boolean }; }; @@ -82,7 +83,7 @@ export * from './typings'; export { useChartTheme } from './hooks/use_chart_theme'; export { useBreadcrumbs } from './hooks/use_breadcrumbs'; export { useTheme } from './hooks/use_theme'; -export { useRulesLink } from './hooks/use_rules_link'; +export { createUseRulesLink } from './hooks/create_use_rules_link'; export { useLinkProps, shouldHandleLinkEvent } from './hooks/use_link_props'; export type { LinkDescriptor } from './hooks/use_link_props'; diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx index 89e5c937bacb6..a26edd4ae67b9 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx @@ -45,6 +45,7 @@ interface RuleStatsState { muted: number; error: number; } + export interface TopAlert { fields: ParsedTechnicalFields & ParsedExperimentalFields; start: number; @@ -69,7 +70,7 @@ const ALERT_STATUS_REGEX = new RegExp( ); function AlertsPage() { - const { core, plugins, ObservabilityPageTemplate } = usePluginContext(); + const { core, plugins, ObservabilityPageTemplate, config } = usePluginContext(); const [alertFilterStatus, setAlertFilterStatus] = useState('' as AlertStatusFilterButton); const { prepend } = core.http.basePath; const refetch = useRef<() => void>(); @@ -137,9 +138,9 @@ function AlertsPage() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // In a future milestone we'll have a page dedicated to rule management in - // observability. For now link to the settings page. - const manageRulesHref = prepend('/app/management/insightsAndAlerting/triggersActions/alerts'); + const manageRulesHref = config.unsafe.rules + ? prepend('/app/observability/rules') + : prepend('/insightsAndAlerting/triggersActions/alerts'); const dynamicIndexPatternsAsyncState = useAsync(async (): Promise => { if (indexNames.length === 0) { diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index 6955f5aee909a..bc9f15bbf4204 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -80,6 +80,7 @@ const withCore = makeDecorator({ alertingExperience: { enabled: true }, cases: { enabled: true }, overviewNext: { enabled: false }, + rules: { enabled: false }, }, }, core: options as CoreStart, diff --git a/x-pack/plugins/observability/public/pages/rules/index.tsx b/x-pack/plugins/observability/public/pages/rules/index.tsx new file mode 100644 index 0000000000000..5728ac1e41edb --- /dev/null +++ b/x-pack/plugins/observability/public/pages/rules/index.tsx @@ -0,0 +1,261 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import moment from 'moment'; +import { + EuiBasicTable, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiText, + EuiBadge, + EuiPopover, + EuiContextMenuPanel, + EuiContextMenuItem, + EuiHorizontalRule, + EuiAutoRefreshButton, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { usePluginContext } from '../../hooks/use_plugin_context'; +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; + +import { useKibana } from '../../utils/kibana_react'; + +const DEFAULT_SEARCH_PAGE_SIZE: number = 25; + +interface RuleState { + data: []; + totalItemsCount: number; +} + +interface Pagination { + index: number; + size: number; +} + +export function RulesPage() { + const { core, ObservabilityPageTemplate } = usePluginContext(); + const { docLinks } = useKibana().services; + const { + http, + notifications: { toasts }, + } = core; + const [rules, setRules] = useState({ data: [], totalItemsCount: 0 }); + const [page, setPage] = useState({ index: 0, size: DEFAULT_SEARCH_PAGE_SIZE }); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + async function loadObservabilityRules() { + const { loadRules } = await import('../../../../triggers_actions_ui/public'); + try { + const response = await loadRules({ + http, + page: { index: 0, size: DEFAULT_SEARCH_PAGE_SIZE }, + typesFilter: [ + 'xpack.uptime.alerts.monitorStatus', + 'xpack.uptime.alerts.tls', + 'xpack.uptime.alerts.tlsCertificate', + 'xpack.uptime.alerts.durationAnomaly', + 'apm.error_rate', + 'apm.transaction_error_rate', + 'apm.transaction_duration', + 'apm.transaction_duration_anomaly', + 'metrics.alert.inventory.threshold', + 'metrics.alert.threshold', + 'logs.alert.document.count', + ], + }); + setRules({ + data: response.data as any, + totalItemsCount: response.total, + }); + } catch (_e) { + toasts.addDanger({ + title: i18n.translate('xpack.observability.rules.loadError', { + defaultMessage: 'Unable to load rules', + }), + }); + } + } + + enum RuleStatus { + enabled = 'enabled', + disabled = 'disabled', + } + + const statuses = Object.values(RuleStatus); + const togglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]); + const popOverButton = ( + + Enabled + + ); + + const panelItems = statuses.map((status) => ( + + {status} + + )); + + useEffect(() => { + loadObservabilityRules(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useBreadcrumbs([ + { + text: i18n.translate('xpack.observability.breadcrumbs.rulesLinkText', { + defaultMessage: 'Rules', + }), + }, + ]); + + const rulesTableColumns = [ + { + field: 'name', + name: i18n.translate('xpack.observability.rules.rulesTable.columns.nameTitle', { + defaultMessage: 'Rule Name', + }), + }, + { + field: 'executionStatus.lastExecutionDate', + name: i18n.translate('xpack.observability.rules.rulesTable.columns.lastRunTitle', { + defaultMessage: 'Last run', + }), + render: (date: Date) => { + if (date) { + return ( + <> + + + + {moment(date).fromNow()} + + + + + ); + } + }, + }, + { + field: 'executionStatus.status', + name: i18n.translate('xpack.observability.rules.rulesTable.columns.lastResponseTitle', { + defaultMessage: 'Last response', + }), + }, + { + field: 'enabled', + name: i18n.translate('xpack.observability.rules.rulesTable.columns.statusTitle', { + defaultMessage: 'Status', + }), + render: (_enabled: boolean) => { + return ( + + + + ); + }, + }, + { + field: '*', + name: i18n.translate('xpack.observability.rules.rulesTable.columns.actionsTitle', { + defaultMessage: 'Actions', + }), + actions: [ + { + name: 'Edit', + isPrimary: true, + description: 'Edit this rule', + icon: 'pencil', + type: 'icon', + onClick: () => {}, + 'data-test-subj': 'action-edit', + }, + ], + }, + ]; + return ( + {i18n.translate('xpack.observability.rulesTitle', { defaultMessage: 'Rules' })} + ), + rightSideItems: [ + + + , + ], + }} + > + + + + + + + + {}} + shortHand + /> + + + + + + { + setPage(changedPage); + }} + selection={{ + selectable: () => true, + onSelectionChange: (selectedItems) => {}, + }} + /> + + + + ); +} diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 38f300af9aa1e..3d2505ed80513 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -46,6 +46,7 @@ import { createNavigationRegistry, NavigationEntry } from './services/navigation import { updateGlobalNavigation } from './update_global_navigation'; import { getExploratoryViewEmbeddable } from './components/shared/exploratory_view/embeddable'; import { createExploratoryViewUrl } from './components/shared/exploratory_view/configurations/utils'; +import { createUseRulesLink } from './hooks/create_use_rules_link'; export type ObservabilityPublicSetup = ReturnType; @@ -92,11 +93,20 @@ export class Plugin path: '/alerts', navLinkStatus: AppNavLinkStatus.hidden, }, + { + id: 'rules', + title: i18n.translate('xpack.observability.rulesLinkTitle', { + defaultMessage: 'Rules', + }), + order: 8002, + path: '/rules', + navLinkStatus: AppNavLinkStatus.hidden, + }, getCasesDeepLinks({ basePath: casesPath, extend: { [CasesDeepLinkId.cases]: { - order: 8002, + order: 8003, navLinkStatus: AppNavLinkStatus.hidden, }, [CasesDeepLinkId.casesCreate]: { @@ -242,6 +252,7 @@ export class Plugin navigation: { registerSections: this.navigationRegistry.registerSections, }, + useRulesLink: createUseRulesLink(config.unsafe.rules.enabled), }; } @@ -270,6 +281,7 @@ export class Plugin }, createExploratoryViewUrl, ExploratoryViewEmbeddable: getExploratoryViewEmbeddable(coreStart, pluginsStart), + useRulesLink: createUseRulesLink(config.unsafe.rules.enabled), }; } } diff --git a/x-pack/plugins/observability/public/routes/index.tsx b/x-pack/plugins/observability/public/routes/index.tsx index 5f85ccd3af7b7..d895f55152ef8 100644 --- a/x-pack/plugins/observability/public/routes/index.tsx +++ b/x-pack/plugins/observability/public/routes/index.tsx @@ -15,6 +15,7 @@ import { LandingPage } from '../pages/landing'; import { OverviewPage } from '../pages/overview'; import { jsonRt } from './json_rt'; import { ObservabilityExploratoryView } from '../components/shared/exploratory_view/obsv_exploratory_view'; +import { RulesPage } from '../pages/rules'; export type RouteParams = DecodeParams; @@ -87,4 +88,11 @@ export const routes = { }, exact: true, }, + '/rules': { + handler: () => { + return ; + }, + params: {}, + exact: true, + }, }; diff --git a/x-pack/plugins/observability/public/update_global_navigation.tsx b/x-pack/plugins/observability/public/update_global_navigation.tsx index 2de282432c158..3e720ac170e8b 100644 --- a/x-pack/plugins/observability/public/update_global_navigation.tsx +++ b/x-pack/plugins/observability/public/update_global_navigation.tsx @@ -48,6 +48,14 @@ export function updateGlobalNavigation({ ? AppNavLinkStatus.visible : AppNavLinkStatus.hidden, }; + case 'rules': + return { + ...link, + navLinkStatus: + config.unsafe.rules.enabled && someVisible + ? AppNavLinkStatus.visible + : AppNavLinkStatus.hidden, + }; default: return link; } diff --git a/x-pack/plugins/observability/public/utils/test_helper.tsx b/x-pack/plugins/observability/public/utils/test_helper.tsx index a3ec446e5c307..214b32ca9efaf 100644 --- a/x-pack/plugins/observability/public/utils/test_helper.tsx +++ b/x-pack/plugins/observability/public/utils/test_helper.tsx @@ -39,6 +39,7 @@ const config = { alertingExperience: { enabled: true }, cases: { enabled: true }, overviewNext: { enabled: false }, + rules: { enabled: false }, }, }; diff --git a/x-pack/plugins/observability/server/index.ts b/x-pack/plugins/observability/server/index.ts index 80da043d26d76..8c81d7d8f9f17 100644 --- a/x-pack/plugins/observability/server/index.ts +++ b/x-pack/plugins/observability/server/index.ts @@ -33,6 +33,7 @@ export const config: PluginConfigDescriptor = { }), unsafe: schema.object({ alertingExperience: schema.object({ enabled: schema.boolean({ defaultValue: true }) }), + rules: schema.object({ enabled: schema.boolean({ defaultValue: false }) }), cases: schema.object({ enabled: schema.boolean({ defaultValue: true }) }), overviewNext: schema.object({ enabled: schema.boolean({ defaultValue: false }) }), }), diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 36ac247cf5c7e..3ba665871e721 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -45,6 +45,8 @@ export function plugin() { export { Plugin }; export * from './plugin'; +// TODO remove this import when we expose the Rules tables as a component +export { loadRules } from './application/lib/rule_api/rules'; export { loadRuleAggregations } from './application/lib/rule_api/aggregate'; export { loadActionTypes } from './application/lib/action_connector_api/connector_types'; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx index 2ca78d6411fda..71553893b6725 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -18,6 +18,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { CLIENT_ALERT_TYPES } from '../../../../common/constants/alerts'; +import { ClientPluginsStart } from '../../../../public/apps/plugin'; + import { ToggleFlyoutTranslations } from './translations'; import { ToggleAlertFlyoutButtonProps } from './alerts_containers'; @@ -43,7 +45,10 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ }) => { const [isOpen, setIsOpen] = useState(false); const kibana = useKibana(); - + const { + services: { observability }, + } = useKibana(); + const manageRulesUrl = observability.useRulesLink(); const hasUptimeWrite = kibana.services.application?.capabilities.uptime?.save ?? false; const monitorStatusAlertContextMenuItem: EuiContextMenuPanelItemDescriptor = { @@ -70,12 +75,7 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ 'aria-label': ToggleFlyoutTranslations.navigateToAlertingUIAriaLabel, 'data-test-subj': 'xpack.uptime.navigateToAlertingUi', name: ( - + Partial = () => { storage: createMockStore(), data: dataPluginMock.createStartContract(), observability: { + useRulesLink: () => ({ href: 'newRuleLink' }), navigation: { // @ts-ignore PageTemplate: EuiPageTemplate, From 2bb237fc4f272d23ecaf2dbfb27eb10ca1d7a943 Mon Sep 17 00:00:00 2001 From: Baturalp Gurdin <9674241+suchcodemuchwow@users.noreply.github.com> Date: Tue, 8 Mar 2022 19:56:00 +0100 Subject: [PATCH 073/140] Add Playwright Service and New User Journeys for Performance tests (#124259) add playwright service and single-user journeys for performance tests - Modifies @kbn/test package to call Playwright Service without constructor - Adds Playwright service to performance tests - Adds following performance user journeys: - Ecommerce Dashboard - Flights Dashboard & edit visualization - Weblogs Dashboard - Promotion Tracking Dashboard Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../functional/performance_sub_playwright.sh | 48 +- .../functional_test_runner.ts | 5 +- .../src/functional_test_runner/index.ts | 9 +- .../src/functional_test_runner/lib/index.ts | 2 +- .../lib/providers/index.ts | 1 + .../lib/providers/provider_collection.ts | 20 +- .../sample_data_view_data_button.test.js.snap | 1 + .../sample_data_view_data_button.js | 4 + x-pack/test/performance/config.playwright.ts | 15 +- .../ecommerce_sample_data/data.json.gz | Bin 0 -> 926531 bytes .../ecommerce_sample_data/mappings.json | 219 + .../promotion_tracking_dashboard.json | 7878 +++++++++++++++++ x-pack/test/performance/page_objects.ts | 2 +- x-pack/test/performance/services.ts | 8 - x-pack/test/performance/services/index.ts | 19 + .../test/performance/services/input_delays.ts | 33 + .../test/performance/services/performance.ts | 248 + .../tests/ftr/reporting_dashboard.ts | 53 - .../tests/playwright/ecommerce_dashboard.ts | 56 + .../tests/playwright/flight_dashboard.ts | 72 + .../test/performance/tests/playwright/home.ts | 55 - .../performance/tests/playwright/index.ts | 16 + .../promotion_tracking_dashboard.ts | 73 + .../performance/tests/playwright/setup.ts | 34 - .../tests/playwright/web_logs_dashboard.ts | 56 + 25 files changed, 8742 insertions(+), 185 deletions(-) create mode 100644 x-pack/test/performance/es_archives/ecommerce_sample_data/data.json.gz create mode 100644 x-pack/test/performance/es_archives/ecommerce_sample_data/mappings.json create mode 100644 x-pack/test/performance/kbn_archives/promotion_tracking_dashboard.json delete mode 100644 x-pack/test/performance/services.ts create mode 100644 x-pack/test/performance/services/index.ts create mode 100644 x-pack/test/performance/services/input_delays.ts create mode 100644 x-pack/test/performance/services/performance.ts delete mode 100644 x-pack/test/performance/tests/ftr/reporting_dashboard.ts create mode 100644 x-pack/test/performance/tests/playwright/ecommerce_dashboard.ts create mode 100644 x-pack/test/performance/tests/playwright/flight_dashboard.ts delete mode 100644 x-pack/test/performance/tests/playwright/home.ts create mode 100644 x-pack/test/performance/tests/playwright/index.ts create mode 100644 x-pack/test/performance/tests/playwright/promotion_tracking_dashboard.ts delete mode 100644 x-pack/test/performance/tests/playwright/setup.ts create mode 100644 x-pack/test/performance/tests/playwright/web_logs_dashboard.ts diff --git a/.buildkite/scripts/steps/functional/performance_sub_playwright.sh b/.buildkite/scripts/steps/functional/performance_sub_playwright.sh index fee171aef9a48..771afe6c78c60 100644 --- a/.buildkite/scripts/steps/functional/performance_sub_playwright.sh +++ b/.buildkite/scripts/steps/functional/performance_sub_playwright.sh @@ -13,29 +13,43 @@ node scripts/es snapshot& esPid=$! -export TEST_PERFORMANCE_PHASE=WARMUP export TEST_ES_URL=http://elastic:changeme@localhost:9200 export TEST_ES_DISABLE_STARTUP=true -export ELASTIC_APM_ACTIVE=false sleep 120 cd "$XPACK_DIR" -# warmup round 1 -checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Phase: WARMUP)" \ - node scripts/functional_tests \ - --debug --bail \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config "test/performance/config.playwright.ts"; - -export TEST_PERFORMANCE_PHASE=TEST -export ELASTIC_APM_ACTIVE=true - -checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Phase: TEST)" \ - node scripts/functional_tests \ - --debug --bail \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --config "test/performance/config.playwright.ts"; +jobId=$(npx uuid) +export TEST_JOB_ID="$jobId" + +journeys=("login" "ecommerce_dashboard" "flight_dashboard" "web_logs_dashboard" "promotion_tracking_dashboard") + +for i in "${journeys[@]}"; do + echo "JOURNEY[${i}] is running" + + export TEST_PERFORMANCE_PHASE=WARMUP + export ELASTIC_APM_ACTIVE=false + export JOURNEY_NAME="${i}" + + checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Journey:${i},Phase: WARMUP)" \ + node scripts/functional_tests \ + --config test/performance/config.playwright.ts \ + --include "test/performance/tests/playwright/${i}.ts" \ + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --debug \ + --bail + + export TEST_PERFORMANCE_PHASE=TEST + export ELASTIC_APM_ACTIVE=true + + checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Journey:${i},Phase: TEST)" \ + node scripts/functional_tests \ + --config test/performance/config.playwright.ts \ + --include "test/performance/tests/playwright/${i}.ts" \ + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --debug \ + --bail +done kill "$esPid" diff --git a/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts b/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts index 9f21d8bd595b5..c8caad3049f14 100644 --- a/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts +++ b/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts @@ -155,8 +155,9 @@ export class FunctionalTestRunner { readProviderSpec(type, providers).map((p) => ({ ...p, fn: skip.includes(p.name) - ? (...args: unknown[]) => { - const result = p.fn(...args); + ? (ctx: any) => { + const result = ProviderCollection.callProviderFn(p.fn, ctx); + if ('then' in result) { throw new Error( `Provider [${p.name}] returns a promise so it can't loaded during test analysis` diff --git a/packages/kbn-test/src/functional_test_runner/index.ts b/packages/kbn-test/src/functional_test_runner/index.ts index e67e72fd5801a..b5d55c28ee9b2 100644 --- a/packages/kbn-test/src/functional_test_runner/index.ts +++ b/packages/kbn-test/src/functional_test_runner/index.ts @@ -7,7 +7,14 @@ */ export { FunctionalTestRunner } from './functional_test_runner'; -export { readConfigFile, Config, EsVersion, Lifecycle, LifecyclePhase } from './lib'; +export { + readConfigFile, + Config, + createAsyncInstance, + EsVersion, + Lifecycle, + LifecyclePhase, +} from './lib'; export type { ScreenshotRecord } from './lib'; export { runFtrCli } from './cli'; export * from './lib/docker_servers'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/index.ts b/packages/kbn-test/src/functional_test_runner/lib/index.ts index e387fd156fe8a..077a62e8e74e5 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/index.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/index.ts @@ -9,7 +9,7 @@ export { Lifecycle } from './lifecycle'; export { LifecyclePhase } from './lifecycle_phase'; export { readConfigFile, Config } from './config'; -export { readProviderSpec, ProviderCollection } from './providers'; +export * from './providers'; // @internal export { runTests, setupMocha } from './mocha'; export * from './test_metadata'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/providers/index.ts b/packages/kbn-test/src/functional_test_runner/lib/providers/index.ts index 10aeca19ba45a..578e41ca8e827 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/providers/index.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/providers/index.ts @@ -8,4 +8,5 @@ export { ProviderCollection } from './provider_collection'; export { readProviderSpec } from './read_provider_spec'; +export { createAsyncInstance } from './async_instance'; export type { Provider } from './read_provider_spec'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts b/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts index 69a5973168f0c..97d7d43417f91 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts @@ -15,6 +15,15 @@ import { createVerboseInstance } from './verbose_instance'; import { GenericFtrService } from '../../public_types'; export class ProviderCollection { + static callProviderFn(providerFn: any, ctx: any) { + if (providerFn.prototype instanceof GenericFtrService) { + const Constructor = providerFn as any as new (ctx: any) => any; + return new Constructor(ctx); + } + + return providerFn(ctx); + } + private readonly instances = new Map(); constructor(private readonly log: ToolingLog, private readonly providers: Providers) {} @@ -59,19 +68,12 @@ export class ProviderCollection { } public invokeProviderFn(provider: (args: any) => any) { - const ctx = { + return ProviderCollection.callProviderFn(provider, { getService: this.getService, hasService: this.hasService, getPageObject: this.getPageObject, getPageObjects: this.getPageObjects, - }; - - if (provider.prototype instanceof GenericFtrService) { - const Constructor = provider as any as new (ctx: any) => any; - return new Constructor(ctx); - } - - return provider(ctx); + }); } private findProvider(type: string, name: string) { diff --git a/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap index 6855e3a327c77..d9e341394ee00 100644 --- a/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/sample_data_view_data_button.test.js.snap @@ -30,6 +30,7 @@ exports[`should render popover when appLinks is not empty 1`] = ` "id": 0, "items": Array [ Object { + "data-test-subj": "viewSampleDataSetecommerce-dashboard", "href": "root/app/dashboards#/view/722b74f0-b882-11e8-a6d9-e546fe2bba5f", "icon": , href: prefixedDashboardPath, onClick: createAppNavigationHandler(dashboardPath), + 'data-test-subj': `viewSampleDataSet${this.props.id}-dashboard`, }, ...additionalItems, ], diff --git a/x-pack/test/performance/config.playwright.ts b/x-pack/test/performance/config.playwright.ts index 9cb7be02ed1b2..1866d97b438bb 100644 --- a/x-pack/test/performance/config.playwright.ts +++ b/x-pack/test/performance/config.playwright.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import uuid from 'uuid'; import { FtrConfigProviderContext } from '@kbn/test'; import { services } from './services'; @@ -14,15 +14,19 @@ import { pageObjects } from './page_objects'; const APM_SERVER_URL = 'https://2fad4006bf784bb8a54e52f4a5862609.apm.us-west1.gcp.cloud.es.io:443'; const APM_PUBLIC_TOKEN = 'Q5q5rWQEw6tKeirBpw'; -export default async function ({ readConfigFile }: FtrConfigProviderContext) { +export default async function ({ readConfigFile, log }: FtrConfigProviderContext) { const functionalConfig = await readConfigFile(require.resolve('../functional/config')); - const testFiles = [require.resolve('./tests/playwright/home.ts')]; + const testFiles = [require.resolve('./tests/playwright')]; + + const testJobId = process.env.TEST_JOB_ID ?? uuid(); + log.info(`👷 JOB ID ${testJobId}👷`); return { testFiles, services, pageObjects, + servicesRequiredForTestAnalysis: ['performance'], servers: functionalConfig.get('servers'), esTestCluster: functionalConfig.get('esTestCluster'), apps: functionalConfig.get('apps'), @@ -32,6 +36,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { }, kbnTestServer: { ...functionalConfig.get('kbnTestServer'), + serverArgs: [...functionalConfig.get('kbnTestServer.serverArgs')], env: { ELASTIC_APM_ACTIVE: process.env.ELASTIC_APM_ACTIVE, ELASTIC_APM_CONTEXT_PROPAGATION_ONLY: 'false', @@ -41,7 +46,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ELASTIC_APM_SECRET_TOKEN: APM_PUBLIC_TOKEN, ELASTIC_APM_GLOBAL_LABELS: Object.entries({ ftrConfig: `x-pack/test/performance/tests/config.playwright`, - performancePhase: process.env.PERF_TEST_PHASE, + performancePhase: process.env.TEST_PERFORMANCE_PHASE, + journeyName: process.env.JOURNEY_NAME, + testJobId, }) .filter(([, v]) => !!v) .reduce((acc, [k, v]) => (acc ? `${acc},${k}=${v}` : `${k}=${v}`), ''), diff --git a/x-pack/test/performance/es_archives/ecommerce_sample_data/data.json.gz b/x-pack/test/performance/es_archives/ecommerce_sample_data/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..a239a98699e6737bd929fd9a1243d46fc1a9d147 GIT binary patch literal 926531 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 (jB+*&s9-}K*wFky#< ziBgE<^D?ym)qcwK5`7`ihR)|Vy*P$zbGvX zUmX1_U4WMK3nDoKJj0swM}1oB699jy1`b6zDfAta#>!zj_GRiArzZ!lAMNA-GEj{m1FPEP> zmZQmzYK^hu6S)HLG9JnZGI9Rmx`4S%#u3{)wUc&l9OtSsBjErGk`2HyTR7oB1RSR*l^KR%1@54fyUegS4-t#Z@RWCEl1k86j6|dcF&*e3c zp@?rOXe%8W<~P`($UZu2CH+w|!f$0(-r$M0UYP

~MijzAg*=T+baNeXk*bN^f`NPG`2`KHnmM=H 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�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

$GojZ_?{3?$vw@HH_4nHU^(U|t6IQKK$6^Ae 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

?fq;E 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≻HoYQ6_xFYRb=8;#+6Id0he1}u40)mJnMIAŧQD2fD@E8V&ZKp~h) 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

+(H(+|jnGb(@W%mnT5?eD?!+!jgh0CYG>?w?n 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 z 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 z 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 ?6n)Zl--DUKZJB1unya{TaBj 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 literal 0 HcmV?d00001 diff --git a/docs/management/cases/images/cases.png b/docs/management/cases/images/cases.png new file mode 100644 index 0000000000000000000000000000000000000000..7b0c551cb69038837cc022d634a877bc1dd073be GIT binary patch literal 82573 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 vi-D^0% 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 {stack-manage-app} > Cases*, then click *Create case*. + +. Give the case a name, add any relevant tags and a description. ++ +TIP: In the `Description` area, you can use +https://www.markdownguide.org/cheat-sheet[Markdown] syntax to create formatted +text. + +. For *External incident management system*, select a connector. If you've +previously added one, that connector displays as the default selection. +Otherwise, the default setting is `No connector selected`. + +. After you've completed all of the required fields, click *Create case*. + +[[add-case-visualization]] +=== Add a visualization + +After you create a case, you can optionally add a visualization. For +example, you can portray event and alert data through charts and graphs. + +[role="screenshot"] +image::images/cases-visualization.png[Cases page] + +To add a visualization to a comment within your case: + +. Click the *Visualization* button. The *Add visualization* dialog appears. + +. Select an existing visualization from your Visualize Library or create a new +visualization. ++ +IMPORTANT: Set an absolute time range for your visualization. This ensures your +visualization doesn't change over time after you save it to your case and +provides important context for viewers. + +. After you've finished creating your visualization, click *Save and return* to +go back to your case. + +. Click *Preview* to see how the visualization will appear in the case comment. + +. Click *Add Comment* to add the visualization to your case. + +After a visualization has been added to a case, you can modify or interact with +it by clicking the *Open Visualization* option in the comment menu. + +[[manage-case]] +=== Manage cases + +In *Management > {stack-manage-app} > Cases*, you can search cases and filter +them by tags, reporter. + +To view a case, click on its name. You can then: + +* Add a new comment. +* Edit existing comments and the description. +* Add a connector. +* Send updates to external systems (if external connections are configured). +* Edit tags. +* Refresh the case to retrieve the latest updates. +* Change the status. +* Close or delete the case. +* Reopen a closed case. \ No newline at end of file diff --git a/docs/management/cases/setup-cases.asciidoc b/docs/management/cases/setup-cases.asciidoc new file mode 100644 index 0000000000000..b0d68a22d9915 --- /dev/null +++ b/docs/management/cases/setup-cases.asciidoc @@ -0,0 +1,28 @@ +[[setup-cases]] +== Configure access to cases + +preview::[] + +To access cases in *{stack-manage-app}*, you must have the appropriate {kib} +privileges: + +[options="header"] +|=== + +| Action | {kib} privileges +| Give full access to manage cases +a| +* `All` for the *Cases* feature under *Management*. +* `All` for the *Actions and Connectors* feature under *Management*. + +NOTE: The `All` *Actions and Connectors* feature privilege is required to +create, add, delete, and modify case connectors and to send updates to external +systems. + +| Give view-only access for cases | `Read` for the *Cases* feature under *Management*. + +| Revoke all access to cases | `None` for the *Cases* feature under *Management*. + +|=== + +For more details, refer to <>. diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc index 6c309d56f2294..908cdc792431c 100644 --- a/docs/user/management.asciidoc +++ b/docs/user/management.asciidoc @@ -78,6 +78,9 @@ You can add and remove remote clusters, and check their connectivity. | Centrally <> across {kib}. Create and <> for triggering actions. +| <> +| Create and manage cases to investigate issues. + | <> | Monitor the generation of reports—PDF, PNG, and CSV—and download reports that you previously generated. A report can contain a dashboard, visualization, saved search, or Canvas workpad. @@ -175,6 +178,8 @@ see the https://www.elastic.co/subscriptions[subscription page]. include::{kib-repo-dir}/management/advanced-options.asciidoc[] +include::{kib-repo-dir}/management/cases/index.asciidoc[] + include::{kib-repo-dir}/management/action-types.asciidoc[] include::{kib-repo-dir}/management/managing-licenses.asciidoc[] From 849542e9f3bc219bdaee047d327b2aa9976b3086 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Tue, 8 Mar 2022 21:06:23 +0100 Subject: [PATCH 076/140] [Lens] data layer & reference layer as separate types (#126885) * [Lens] data layer & reference layer as separate types * types better (thanks Marco ) * CR comment --- x-pack/plugins/lens/common/constants.ts | 5 +- .../data_layer_config.ts} | 48 ++-- .../xy_chart/layer_config/index.ts | 12 + .../reference_line_layer_config.ts | 64 +++++ .../common/expressions/xy_chart/xy_args.ts | 4 +- .../common/expressions/xy_chart/xy_chart.ts | 2 +- x-pack/plugins/lens/common/types.ts | 10 +- x-pack/plugins/lens/public/expressions.ts | 8 +- .../__snapshots__/to_expression.test.ts.snap | 2 +- .../axes_configuration.test.ts | 4 +- .../xy_visualization/axes_configuration.ts | 6 +- .../xy_visualization/color_assignment.test.ts | 4 +- .../xy_visualization/color_assignment.ts | 1 + .../xy_visualization/expression.test.tsx | 48 ++-- .../public/xy_visualization/expression.tsx | 10 +- .../expression_reference_lines.test.tsx | 21 +- .../expression_reference_lines.tsx | 6 +- .../get_legend_action.test.tsx | 4 +- .../xy_visualization/get_legend_action.tsx | 4 +- .../reference_line_helpers.test.ts | 60 ++--- .../reference_line_helpers.tsx | 27 +-- .../public/xy_visualization/state_helpers.ts | 22 +- .../xy_visualization/to_expression.test.ts | 3 - .../public/xy_visualization/to_expression.ts | 223 +++++++++++------- .../xy_visualization/visualization.test.ts | 53 +++-- .../public/xy_visualization/visualization.tsx | 71 +++--- .../visualization_helpers.tsx | 42 +++- .../lens/public/xy_visualization/x_domain.tsx | 6 +- .../xy_config_panel/color_picker.tsx | 4 +- .../xy_config_panel/index.tsx | 18 +- .../xy_config_panel/layer_header.tsx | 4 +- .../visual_options_popover/index.tsx | 12 +- .../visual_options_popover.test.tsx | 27 ++- .../xy_config_panel/xy_config_panel.test.tsx | 6 +- .../xy_visualization/xy_suggestions.test.ts | 27 ++- .../public/xy_visualization/xy_suggestions.ts | 11 +- .../lens/server/expressions/expressions.ts | 6 +- 37 files changed, 528 insertions(+), 357 deletions(-) rename x-pack/plugins/lens/common/expressions/xy_chart/{layer_config.ts => layer_config/data_layer_config.ts} (72%) create mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts create mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/layer_config/reference_line_layer_config.ts diff --git a/x-pack/plugins/lens/common/constants.ts b/x-pack/plugins/lens/common/constants.ts index bd507be52e2ab..1504e33ecacab 100644 --- a/x-pack/plugins/lens/common/constants.ts +++ b/x-pack/plugins/lens/common/constants.ts @@ -7,7 +7,6 @@ import rison from 'rison-node'; import type { TimeRange } from '../../../../src/plugins/data/common/query'; -import { LayerType } from './types'; export const PLUGIN_ID = 'lens'; export const APP_ID = 'lens'; @@ -43,10 +42,10 @@ export const LegendDisplay = { HIDE: 'hide', } as const; -export const layerTypes: Record = { +export const layerTypes = { DATA: 'data', REFERENCELINE: 'referenceLine', -}; +} as const; // might collide with user-supplied field names, try to make as unique as possible export const DOCUMENT_FIELD_NAME = '___records___'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/data_layer_config.ts similarity index 72% rename from x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts rename to x-pack/plugins/lens/common/expressions/xy_chart/layer_config/data_layer_config.ts index ff3d50a13a06d..322edccba19e3 100644 --- a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts +++ b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/data_layer_config.ts @@ -5,30 +5,28 @@ * 2.0. */ -import type { PaletteOutput } from '../../../../../../src/plugins/charts/common'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; -import type { LayerType } from '../../types'; -import { layerTypes } from '../../constants'; -import { axisConfig, YConfig } from './axis_config'; -import type { SeriesType } from './series_type'; +import { layerTypes } from '../../../constants'; +import type { PaletteOutput } from '../../../../../../../src/plugins/charts/common'; +import type { ExpressionFunctionDefinition } from '../../../../../../../src/plugins/expressions/common'; +import { axisConfig, YConfig } from '../axis_config'; +import type { SeriesType } from '../series_type'; -export interface XYLayerConfig { - hide?: boolean; +export interface XYDataLayerConfig { layerId: string; - xAccessor?: string; + layerType: typeof layerTypes.DATA; accessors: string[]; - yConfig?: YConfig[]; seriesType: SeriesType; + xAccessor?: string; + hide?: boolean; + yConfig?: YConfig[]; splitAccessor?: string; palette?: PaletteOutput; - layerType: LayerType; } - -export interface ValidLayer extends XYLayerConfig { - xAccessor: NonNullable; +export interface ValidLayer extends XYDataLayerConfig { + xAccessor: NonNullable; } -export type LayerArgs = XYLayerConfig & { +export type DataLayerArgs = XYDataLayerConfig & { columnToLabel?: string; // Actually a JSON key-value pair yScaleType: 'time' | 'linear' | 'log' | 'sqrt'; xScaleType: 'time' | 'linear' | 'ordinal'; @@ -37,17 +35,17 @@ export type LayerArgs = XYLayerConfig & { palette: PaletteOutput; }; -export type LayerConfigResult = LayerArgs & { type: 'lens_xy_layer' }; +export type DataLayerConfigResult = DataLayerArgs & { type: 'lens_xy_data_layer' }; -export const layerConfig: ExpressionFunctionDefinition< - 'lens_xy_layer', +export const dataLayerConfig: ExpressionFunctionDefinition< + 'lens_xy_data_layer', null, - LayerArgs, - LayerConfigResult + DataLayerArgs, + DataLayerConfigResult > = { - name: 'lens_xy_layer', + name: 'lens_xy_data_layer', aliases: [], - type: 'lens_xy_layer', + type: 'lens_xy_data_layer', help: `Configure a layer in the xy chart`, inputTypes: ['null'], args: { @@ -60,7 +58,7 @@ export const layerConfig: ExpressionFunctionDefinition< types: ['string'], help: '', }, - layerType: { types: ['string'], options: Object.values(layerTypes), help: '' }, + layerType: { types: ['string'], options: [layerTypes.DATA], help: '' }, seriesType: { types: ['string'], options: [ @@ -115,9 +113,9 @@ export const layerConfig: ExpressionFunctionDefinition< types: ['palette'], }, }, - fn: function fn(input: unknown, args: LayerArgs) { + fn: function fn(input: unknown, args: DataLayerArgs) { return { - type: 'lens_xy_layer', + type: 'lens_xy_data_layer', ...args, }; }, diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts new file mode 100644 index 0000000000000..0b27ce7d6ed85 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { XYDataLayerConfig } from './data_layer_config'; +import { XYReferenceLineLayerConfig } from './reference_line_layer_config'; +export * from './data_layer_config'; +export * from './reference_line_layer_config'; + +export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/reference_line_layer_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/reference_line_layer_config.ts new file mode 100644 index 0000000000000..6e241f8b8db65 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/reference_line_layer_config.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ExpressionFunctionDefinition } from '../../../../../../../src/plugins/expressions/common'; +import { layerTypes } from '../../../constants'; +import { YConfig } from '../axis_config'; + +export interface XYReferenceLineLayerConfig { + layerId: string; + layerType: typeof layerTypes.REFERENCELINE; + accessors: string[]; + yConfig?: YConfig[]; +} +export type ReferenceLineLayerArgs = XYReferenceLineLayerConfig & { + columnToLabel?: string; +}; +export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { + type: 'lens_xy_referenceLine_layer'; +}; + +export const referenceLineLayerConfig: ExpressionFunctionDefinition< + 'lens_xy_referenceLine_layer', + null, + ReferenceLineLayerArgs, + ReferenceLineLayerConfigResult +> = { + name: 'lens_xy_referenceLine_layer', + aliases: [], + type: 'lens_xy_referenceLine_layer', + help: `Configure a layer in the xy chart`, + inputTypes: ['null'], + args: { + layerId: { + types: ['string'], + help: '', + }, + layerType: { types: ['string'], options: [layerTypes.REFERENCELINE], help: '' }, + accessors: { + types: ['string'], + help: 'The columns to display on the y axis.', + multi: true, + }, + yConfig: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + types: ['lens_xy_yConfig' as any], + help: 'Additional configuration for y axes', + multi: true, + }, + columnToLabel: { + types: ['string'], + help: 'JSON key-value pairs of column ID to label', + }, + }, + fn: function fn(input: unknown, args: ReferenceLineLayerArgs) { + return { + type: 'lens_xy_referenceLine_layer', + ...args, + }; + }, +}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts index 4e712f7ca3bf4..1334c1149f47b 100644 --- a/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts +++ b/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts @@ -8,7 +8,7 @@ import type { AxisExtentConfigResult, AxisTitlesVisibilityConfigResult } from './axis_config'; import type { FittingFunction } from './fitting_function'; import type { GridlinesConfigResult } from './grid_lines_config'; -import type { LayerArgs } from './layer_config'; +import type { DataLayerArgs } from './layer_config'; import type { LegendConfigResult } from './legend_config'; import type { TickLabelsConfigResult } from './tick_labels_config'; import type { LabelsOrientationConfigResult } from './labels_orientation_config'; @@ -27,7 +27,7 @@ export interface XYArgs { yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; valueLabels: ValueLabelConfig; - layers: LayerArgs[]; + layers: DataLayerArgs[]; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts index 00baf894de046..d3fb2fe7a6917 100644 --- a/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts +++ b/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts @@ -117,7 +117,7 @@ export const xyChart: ExpressionFunctionDefinition< }, layers: { // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_layer'] as any, + types: ['lens_xy_data_layer', 'lens_xy_referenceLine_layer'] as any, help: 'Layers of visual series', multi: true, }, diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index 0b2b5d5d739d0..8c333fd0daa6a 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -15,7 +15,13 @@ import type { import type { Datatable } from '../../../../src/plugins/expressions/common'; import type { PaletteContinuity } from '../../../../src/plugins/charts/common'; import type { PaletteOutput } from '../../../../src/plugins/charts/common'; -import { CategoryDisplay, LegendDisplay, NumberDisplay, PieChartTypes } from './constants'; +import { + CategoryDisplay, + layerTypes, + LegendDisplay, + NumberDisplay, + PieChartTypes, +} from './constants'; export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; @@ -73,7 +79,7 @@ export type RequiredPaletteParamTypes = Required & { maxSteps?: number; }; -export type LayerType = 'data' | 'referenceLine'; +export type LayerType = typeof layerTypes[keyof typeof layerTypes]; // Shared by XY Chart and Heatmap as for now export type ValueLabelConfig = 'hide' | 'inside' | 'outside'; diff --git a/x-pack/plugins/lens/public/expressions.ts b/x-pack/plugins/lens/public/expressions.ts index 2e5a30345633f..87445ff6d6882 100644 --- a/x-pack/plugins/lens/public/expressions.ts +++ b/x-pack/plugins/lens/public/expressions.ts @@ -14,7 +14,10 @@ import { } from '../common/expressions/xy_chart/axis_config'; import { gridlinesConfig } from '../common/expressions/xy_chart/grid_lines_config'; import { labelsOrientationConfig } from '../common/expressions/xy_chart/labels_orientation_config'; -import { layerConfig } from '../common/expressions/xy_chart/layer_config'; +import { + dataLayerConfig, + referenceLineLayerConfig, +} from '../common/expressions/xy_chart/layer_config'; import { legendConfig } from '../common/expressions/xy_chart/legend_config'; import { tickLabelsConfig } from '../common/expressions/xy_chart/tick_labels_config'; import { xyChart } from '../common/expressions/xy_chart/xy_chart'; @@ -43,7 +46,8 @@ export const setupExpressions = ( counterRate, metricChart, yAxisConfig, - layerConfig, + dataLayerConfig, + referenceLineLayerConfig, formatColumn, legendConfig, renameColumns, diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index 2af871d581039..a2be6c1f43bdf 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -127,7 +127,7 @@ Object { "linear", ], }, - "function": "lens_xy_layer", + "function": "lens_xy_data_layer", "type": "function", }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index 355374165c788..ac3e224663ce4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { LayerArgs } from '../../common/expressions'; +import { DataLayerArgs } from '../../common/expressions'; import { layerTypes } from '../../common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import { getAxesConfiguration } from './axes_configuration'; @@ -219,7 +219,7 @@ describe('axes_configuration', () => { }, }; - const sampleLayer: LayerArgs = { + const sampleLayer: DataLayerArgs = { layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 9ac0171a51084..7adc803f31e9f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -6,7 +6,7 @@ */ import { FormatFactory } from '../../common'; -import { AxisExtentConfig, XYLayerConfig } from '../../common/expressions'; +import { AxisExtentConfig, XYDataLayerConfig } from '../../common/expressions'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { IFieldFormat, @@ -33,7 +33,7 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function groupAxesByType(layers: XYLayerConfig[], tables?: Record) { +export function groupAxesByType(layers: XYDataLayerConfig[], tables?: Record) { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; @@ -97,7 +97,7 @@ export function groupAxesByType(layers: XYLayerConfig[], tables?: Record, formatFactory?: FormatFactory diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts index 4157eabfad82d..9b29401d72a95 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts @@ -7,11 +7,11 @@ import { getColorAssignments } from './color_assignment'; import type { FormatFactory, LensMultiTable } from '../../common'; -import type { LayerArgs } from '../../common/expressions'; +import type { DataLayerArgs } from '../../common/expressions'; import { layerTypes } from '../../common'; describe('color_assignment', () => { - const layers: LayerArgs[] = [ + const layers: DataLayerArgs[] = [ { yScaleType: 'linear', xScaleType: 'linear', diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index 2c56725438421..82c1106e72a08 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -122,6 +122,7 @@ export function getAccessorColorConfig( if (isReferenceLayer(layer)) { return getReferenceLineAccessorColorConfig(layer); } + const layerContainsSplits = Boolean(layer.splitAccessor); const currentPalette: PaletteOutput = layer.palette || { type: 'palette', name: 'default' }; const totalSeriesCount = colorAssignments[currentPalette.name]?.totalSeriesCount; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx index 6bee021b36de6..654a0f1b94a14 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -27,13 +27,13 @@ import type { LensMultiTable } from '../../common'; import { layerTypes } from '../../common'; import { xyChart } from '../../common/expressions'; import { - layerConfig, + dataLayerConfig, legendConfig, tickLabelsConfig, gridlinesConfig, XYArgs, LegendConfig, - LayerArgs, + DataLayerArgs, AxesSettingsConfig, XYChartProps, labelsOrientationConfig, @@ -212,7 +212,7 @@ const dateHistogramData: LensMultiTable = { }, }; -const dateHistogramLayer: LayerArgs = { +const dateHistogramLayer: DataLayerArgs = { layerId: 'timeLayer', layerType: layerTypes.DATA, hide: false, @@ -254,7 +254,7 @@ const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable => ({ rows, }); -const sampleLayer: LayerArgs = { +const sampleLayer: DataLayerArgs = { layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', @@ -268,7 +268,7 @@ const sampleLayer: LayerArgs = { palette: mockPaletteOutput, }; -const createArgsWithLayers = (layers: LayerArgs[] = [sampleLayer]): XYArgs => ({ +const createArgsWithLayers = (layers: DataLayerArgs[] = [sampleLayer]): XYArgs => ({ xTitle: '', yTitle: '', yRightTitle: '', @@ -392,8 +392,8 @@ describe('xy_expression', () => { }); }); - test('layerConfig produces the correct arguments', () => { - const args: LayerArgs = { + test('dataLayerConfig produces the correct arguments', () => { + const args: DataLayerArgs = { layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', @@ -406,10 +406,10 @@ describe('xy_expression', () => { palette: mockPaletteOutput, }; - const result = layerConfig.fn(null, args, createMockExecutionContext()); + const result = dataLayerConfig.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ - type: 'lens_xy_layer', + type: 'lens_xy_data_layer', ...args, }); }); @@ -556,7 +556,7 @@ describe('xy_expression', () => { }); describe('date range', () => { - const timeSampleLayer: LayerArgs = { + const timeSampleLayer: DataLayerArgs = { layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', @@ -649,7 +649,7 @@ describe('xy_expression', () => { }); describe('axis time', () => { - const defaultTimeLayer: LayerArgs = { + const defaultTimeLayer: DataLayerArgs = { layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', @@ -707,7 +707,7 @@ describe('xy_expression', () => { }); test('it should disable the new time axis for a vertical bar with break down dimension', () => { const { data } = sampleArgs(); - const timeLayer: LayerArgs = { + const timeLayer: DataLayerArgs = { ...defaultTimeLayer, seriesType: 'bar', }; @@ -734,7 +734,7 @@ describe('xy_expression', () => { test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { const { data } = sampleArgs(); - const timeLayer: LayerArgs = { + const timeLayer: DataLayerArgs = { ...defaultTimeLayer, seriesType: 'bar_stacked', }; @@ -1227,7 +1227,7 @@ describe('xy_expression', () => { test('onBrushEnd returns correct context data for number histogram data', () => { const { args } = sampleArgs(); - const numberLayer: LayerArgs = { + const numberLayer: DataLayerArgs = { layerId: 'numberLayer', layerType: layerTypes.DATA, hide: false, @@ -1436,7 +1436,7 @@ describe('xy_expression', () => { test('onElementClick returns correct context data for numeric histogram', () => { const { args } = sampleArgs(); - const numberLayer: LayerArgs = { + const numberLayer: DataLayerArgs = { layerId: 'numberLayer', layerType: layerTypes.DATA, hide: false, @@ -1757,7 +1757,7 @@ describe('xy_expression', () => { test('it applies histogram mode to the series for single series', () => { const { data, args } = sampleArgs(); - const firstLayer: LayerArgs = { + const firstLayer: DataLayerArgs = { ...args.layers[0], accessors: ['b'], seriesType: 'bar', @@ -1772,7 +1772,7 @@ describe('xy_expression', () => { test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => { const { data, args } = sampleArgs(); - const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true }; + const firstLayer: DataLayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true }; delete firstLayer.splitAccessor; const component = shallow( @@ -1783,9 +1783,17 @@ describe('xy_expression', () => { test('it applies histogram mode to more than one the series for unstacked line/area chart', () => { const { data, args } = sampleArgs(); - const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true }; + const firstLayer: DataLayerArgs = { + ...args.layers[0], + seriesType: 'line', + isHistogram: true, + }; delete firstLayer.splitAccessor; - const secondLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true }; + const secondLayer: DataLayerArgs = { + ...args.layers[0], + seriesType: 'line', + isHistogram: true, + }; delete secondLayer.splitAccessor; const component = shallow( { toDate: new Date('2019-01-03T05:00:00.000Z'), }, }; - const timeSampleLayer: LayerArgs = { + const timeSampleLayer: DataLayerArgs = { layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index fb8ad127a394e..5bed5d35bc302 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -53,7 +53,7 @@ import { EmptyPlaceholder } from '../../../../../src/plugins/charts/public'; import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; import type { ILensInterpreterRenderHandlers, LensFilterEvent, LensBrushEvent } from '../types'; import type { LensMultiTable, FormatFactory } from '../../common'; -import type { LayerArgs, SeriesType, XYChartProps } from '../../common/expressions'; +import type { DataLayerArgs, SeriesType, XYChartProps } from '../../common/expressions'; import { visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart, getSeriesColor } from './state_helpers'; @@ -77,7 +77,7 @@ import { ReferenceLineAnnotations, } from './expression_reference_lines'; import { computeOverallDataDomain } from './reference_line_helpers'; -import { isDataLayer, isReferenceLayer } from './visualization_helpers'; +import { getReferenceLayers, isDataLayer } from './visualization_helpers'; declare global { interface Window { @@ -255,7 +255,7 @@ export function XYChart({ const layersById = filteredLayers.reduce((memo, layer) => { memo[layer.layerId] = layer; return memo; - }, {} as Record); + }, {} as Record); const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { datatables: Object.values(data.tables), @@ -349,7 +349,7 @@ export function XYChart({ ); }; - const referenceLineLayers = layers.filter((layer) => isReferenceLayer(layer)); + const referenceLineLayers = getReferenceLayers(layers); const referenceLinePaddings = getReferenceLineRequiredPaddings(referenceLineLayers, yAxesMap); const getYAxesStyle = (groupId: 'left' | 'right') => { @@ -985,7 +985,7 @@ export function XYChart({ ); } -function getFilteredLayers(layers: LayerArgs[], data: LensMultiTable) { +function getFilteredLayers(layers: DataLayerArgs[], data: LensMultiTable) { return layers.filter((layer) => { const { layerId, xAccessor, accessors, splitAccessor } = layer; return ( diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.test.tsx index 85d5dd362a431..4ec38f31b85af 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.test.tsx @@ -8,11 +8,10 @@ import { LineAnnotation, RectAnnotation } from '@elastic/charts'; import { shallow } from 'enzyme'; import React from 'react'; -import { PaletteOutput } from 'src/plugins/charts/common'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { FieldFormat } from 'src/plugins/field_formats/common'; -import { layerTypes, LensMultiTable } from '../../common'; -import { LayerArgs, YConfig } from '../../common/expressions'; +import { LensMultiTable } from '../../common'; +import { ReferenceLineLayerArgs, YConfig } from '../../common/expressions'; import { ReferenceLineAnnotations, ReferenceLineAnnotationsProps, @@ -20,12 +19,6 @@ import { const paletteService = chartPluginMock.createPaletteRegistry(); -const mockPaletteOutput: PaletteOutput = { - type: 'palette', - name: 'mock', - params: {}, -}; - const row: Record = { xAccessorFirstId: 1, xAccessorSecondId: 2, @@ -57,18 +50,12 @@ const histogramData: LensMultiTable = { }, }; -function createLayers(yConfigs: LayerArgs['yConfig']): LayerArgs[] { +function createLayers(yConfigs: ReferenceLineLayerArgs['yConfig']): ReferenceLineLayerArgs[] { return [ { layerId: 'firstLayer', - layerType: layerTypes.REFERENCE_LINE, - hide: false, - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - seriesType: 'bar_stacked', + layerType: 'referenceLine', accessors: (yConfigs || []).map(({ forAccessor }) => forAccessor), - palette: mockPaletteOutput, yConfig: yConfigs, }, ]; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.tsx b/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.tsx index a20f70ad6f4eb..d9a6a84bb5383 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.tsx @@ -13,7 +13,7 @@ import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from ' import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { FieldFormat } from 'src/plugins/field_formats/common'; import { euiLightVars } from '@kbn/ui-theme'; -import type { LayerArgs, YConfig } from '../../common/expressions'; +import type { ReferenceLineLayerArgs, YConfig } from '../../common/expressions'; import type { LensMultiTable } from '../../common/types'; import { hasIcon } from './xy_config_panel/shared/icon_select'; @@ -55,7 +55,7 @@ export const computeChartMargins = ( // Note: it does not take into consideration whether the reference line is in view or not export const getReferenceLineRequiredPaddings = ( - referenceLineLayers: LayerArgs[], + referenceLineLayers: ReferenceLineLayerArgs[], axesMap: Record<'left' | 'right', unknown> ) => { // collect all paddings for the 4 axis: if any text is detected double it. @@ -181,7 +181,7 @@ function getMarkerToShow( } export interface ReferenceLineAnnotationsProps { - layers: LayerArgs[]; + layers: ReferenceLineLayerArgs[]; data: LensMultiTable; formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; paletteService: PaletteRegistry; diff --git a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx index c15c0916bee0b..faa3ecc976d9b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx @@ -12,7 +12,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ComponentType, ReactWrapper } from 'enzyme'; import type { LensMultiTable } from '../../common'; import { layerTypes } from '../../common'; -import type { LayerArgs } from '../../common/expressions'; +import type { DataLayerArgs } from '../../common/expressions'; import { getLegendAction } from './get_legend_action'; import { LegendActionPopover } from '../shared_components'; @@ -27,7 +27,7 @@ const sampleLayer = { xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, -} as LayerArgs; +} as DataLayerArgs; const tables = { first: { diff --git a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx index 0603328ee5bb3..00532314e045b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx @@ -9,11 +9,11 @@ import React from 'react'; import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts'; import type { LensFilterEvent } from '../types'; import type { LensMultiTable, FormatFactory } from '../../common'; -import type { LayerArgs } from '../../common/expressions'; +import type { DataLayerArgs } from '../../common/expressions'; import { LegendActionPopover } from '../shared_components'; export const getLegendAction = ( - filteredLayers: LayerArgs[], + filteredLayers: DataLayerArgs[], tables: LensMultiTable['tables'], onFilter: (data: LensFilterEvent['data']) => void, formatFactory: FormatFactory, diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts index 9f48b8c8c36e4..9def75615e6c8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { XYLayerConfig } from '../../common/expressions'; +import { XYDataLayerConfig } from '../../common/expressions'; import { FramePublicAPI } from '../types'; import { computeOverallDataDomain, getStaticValue } from './reference_line_helpers'; @@ -51,7 +51,7 @@ describe('reference_line helpers', () => { // accessor id has no hit in data expect( getStaticValue( - [{ layerId: 'id-a', seriesType: 'area' } as XYLayerConfig], // missing xAccessor for groupId == x + [{ layerId: 'id-a', seriesType: 'area' } as XYDataLayerConfig], // missing xAccessor for groupId == x 'x', { activeData: getActiveData([ @@ -69,7 +69,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['d'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], // missing hit of accessor "d" in data 'yLeft', { @@ -88,7 +88,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], // missing yConfig fallbacks to left axis, but the requested group is yRight 'yRight', { @@ -107,7 +107,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], // same as above with x groupId 'x', { @@ -130,7 +130,7 @@ describe('reference_line helpers', () => { layerType: 'data', accessors: ['a'], yConfig: [{ forAccessor: 'a', axisMode: 'right' }], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'yRight', { @@ -155,7 +155,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'yLeft', { @@ -178,7 +178,7 @@ describe('reference_line helpers', () => { layerType: 'data', accessors: ['a'], yConfig: [{ forAccessor: 'a', axisMode: 'right' }], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'yRight', { @@ -205,7 +205,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'yLeft', { activeData: tables }, @@ -220,7 +220,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'yRight', { activeData: tables }, @@ -243,7 +243,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'yLeft', { activeData: tables }, @@ -258,7 +258,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'yRight', { activeData: tables }, @@ -277,7 +277,7 @@ describe('reference_line helpers', () => { layerType: 'data', xAccessor: 'a', accessors: [], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'x', // this is influenced by the callback { @@ -300,7 +300,7 @@ describe('reference_line helpers', () => { layerType: 'data', xAccessor: 'a', accessors: [], - } as XYLayerConfig, + } as XYDataLayerConfig, ], 'x', { @@ -324,7 +324,7 @@ describe('reference_line helpers', () => { for (const seriesType of ['bar_stacked', 'bar_horizontal_stacked', 'area_stacked']) expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYLayerConfig], + [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], ['a', 'b', 'c'], getActiveData([ { @@ -350,7 +350,7 @@ describe('reference_line helpers', () => { ]) expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYLayerConfig], + [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], ['a', 'b', 'c'], getActiveData([ { @@ -375,7 +375,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: [{ a: 25, b: 100, c: 100 }] }, @@ -389,7 +389,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { @@ -425,7 +425,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, @@ -443,7 +443,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: nonStackedSeries, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: stackedSeries, accessors: ['d', 'e', 'f'] }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: [{ a: 100, b: 100, c: 100 }] }, @@ -465,7 +465,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] }, { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['a', 'b', 'd', 'e'], getActiveData([ { @@ -492,7 +492,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['c'] }, { layerId: 'id-b', seriesType, accessors: ['f'] }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['c', 'f'], getActiveData([ { @@ -520,7 +520,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] }, { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['a', 'b', 'd', 'e'], getActiveData([ { @@ -549,7 +549,7 @@ describe('reference_line helpers', () => { layerId: 'id-a', seriesType: 'area_stacked', accessors: ['a', 'b', 'c'], - } as XYLayerConfig, + } as XYDataLayerConfig, ], ['a', 'b', 'c'], getActiveData([ @@ -568,7 +568,13 @@ describe('reference_line helpers', () => { ).toEqual({ min: 0, max: 200 }); // it is stacked, so max is the sum and 0 is the fallback expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'] } as XYLayerConfig], + [ + { + layerId: 'id-a', + seriesType: 'area', + accessors: ['a', 'b', 'c'], + } as XYDataLayerConfig, + ], ['a', 'b', 'c'], getActiveData([ { @@ -602,7 +608,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: 'line', accessors: ['d', 'e', 'f'] }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['a', 'b'], getActiveData([{ id: 'id-c', rows: [{ a: 100, b: 100 }] }]) // mind the layer id here ) @@ -613,7 +619,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: 'bar', accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: 'bar_stacked' }, - ] as XYLayerConfig[], + ] as XYDataLayerConfig[], ['a', 'b'], getActiveData([]) ) diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index 42e46b65f5c1f..05a81b15efaba 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -8,7 +8,7 @@ import { groupBy, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; import { layerTypes } from '../../common'; -import type { XYLayerConfig, YConfig } from '../../common/expressions'; +import type { XYDataLayerConfig, XYLayerConfig, YConfig } from '../../common/expressions'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { AccessorConfig, DatasourcePublicAPI, FramePublicAPI, Visualization } from '../types'; import { groupAxesByType } from './axes_configuration'; @@ -17,7 +17,7 @@ import type { XYState } from './types'; import { checkScaleOperation, getAxisName, - isDataLayer, + getDataLayers, isNumericMetric, } from './visualization_helpers'; import { generateId } from '../id_generator'; @@ -41,9 +41,7 @@ export function getGroupsToShow - isDataLayer({ layerType }) - ); + const dataLayers = getDataLayers(state.layers); const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables); return referenceLayers .filter(({ label, config }: T) => groupsAvailable[label] || config?.length) @@ -62,9 +60,7 @@ export function getGroupsRelatedToData( if (!state) { return []; } - const dataLayers = state.layers.filter(({ layerType = layerTypes.DATA }) => - isDataLayer({ layerType }) - ); + const dataLayers = getDataLayers(state.layers); const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables); return referenceLayers.filter(({ label }: T) => groupsAvailable[label]); } @@ -72,7 +68,7 @@ export function getGroupsRelatedToData( * Returns a dictionary with the groups filled in all the data layers */ export function getGroupsAvailableInData( - dataLayers: XYState['layers'], + dataLayers: XYDataLayerConfig[], datasourceLayers: Record, tables: Record | undefined ) { @@ -88,10 +84,10 @@ export function getGroupsAvailableInData( } export function getStaticValue( - dataLayers: XYState['layers'], + dataLayers: XYDataLayerConfig[], groupId: 'x' | 'yLeft' | 'yRight', { activeData }: Pick, - layerHasNumberHistogram: (layer: XYLayerConfig) => boolean + layerHasNumberHistogram: (layer: XYDataLayerConfig) => boolean ) { const fallbackValue = 100; if (!activeData) { @@ -124,7 +120,7 @@ export function getStaticValue( function getAccessorCriteriaForGroup( groupId: 'x' | 'yLeft' | 'yRight', - dataLayers: XYState['layers'], + dataLayers: XYDataLayerConfig[], activeData: FramePublicAPI['activeData'] ) { switch (groupId) { @@ -158,7 +154,7 @@ function getAccessorCriteriaForGroup( } export function computeOverallDataDomain( - dataLayers: Array>, + dataLayers: XYDataLayerConfig[], accessorIds: string[], activeData: NonNullable, allowStacking: boolean = true @@ -222,7 +218,7 @@ export function computeOverallDataDomain( } function computeStaticValueForGroup( - dataLayers: Array>, + dataLayers: XYDataLayerConfig[], accessorIds: string[], activeData: NonNullable, minZeroOrNegativeBase: boolean = true, @@ -275,8 +271,7 @@ export const getReferenceSupportedLayer = ( frame?.datasourceLayers || {}, frame?.activeData ); - const dataLayers = - state?.layers.filter(({ layerType = layerTypes.DATA }) => isDataLayer({ layerType })) || []; + const dataLayers = getDataLayers(state?.layers || []); const filledDataLayers = dataLayers.filter( ({ accessors, xAccessor }) => accessors.length || xAccessor ); diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index 14a82011cb526..f37ff5460f314 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -9,6 +9,7 @@ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; import type { SeriesType, XYLayerConfig, YConfig, ValidLayer } from '../../common/expressions'; import { visualizationTypes } from './types'; +import { getDataLayers, isDataLayer } from './visualization_helpers'; export function isHorizontalSeries(seriesType: SeriesType) { return ( @@ -30,8 +31,8 @@ export function isStackedChart(seriesType: SeriesType) { return seriesType.includes('stacked'); } -export function isHorizontalChart(layers: Array<{ seriesType: SeriesType }>) { - return layers.every((l) => isHorizontalSeries(l.seriesType)); +export function isHorizontalChart(layers: XYLayerConfig[]) { + return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); } export function getIconForSeries(type: SeriesType): EuiIconType { @@ -45,7 +46,7 @@ export function getIconForSeries(type: SeriesType): EuiIconType { } export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { - if (layer.splitAccessor) { + if (isDataLayer(layer) && layer.splitAccessor) { return null; } return ( @@ -55,13 +56,14 @@ export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { export const getColumnToLabelMap = (layer: XYLayerConfig, datasource: DatasourcePublicAPI) => { const columnToLabel: Record = {}; - - layer.accessors.concat(layer.splitAccessor ? [layer.splitAccessor] : []).forEach((accessor) => { - const operation = datasource.getOperationForColumnId(accessor); - if (operation?.label) { - columnToLabel[accessor] = operation.label; - } - }); + layer.accessors + .concat(isDataLayer(layer) && layer.splitAccessor ? [layer.splitAccessor] : []) + .forEach((accessor) => { + const operation = datasource.getOperationForColumnId(accessor); + if (operation?.label) { + columnToLabel[accessor] = operation.label; + } + }); return columnToLabel; }; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index c0dfbd9986087..c73e6c42b53d2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -343,9 +343,6 @@ describe('#toExpression', () => { { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, - seriesType: 'area', - splitAccessor: 'd', - xAccessor: 'a', accessors: ['b', 'c'], yConfig: [{ forAccessor: 'a' }], }, diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 8fa76d0b997d2..ec93295d647bf 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -11,12 +11,17 @@ import { PaletteRegistry } from 'src/plugins/charts/public'; import { State } from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import type { ValidLayer, XYLayerConfig } from '../../common/expressions'; +import type { + ValidLayer, + XYLayerConfig, + XYReferenceLineLayerConfig, + YConfig, +} from '../../common/expressions'; import { layerTypes } from '../../common'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { defaultReferenceLineColor } from './color_assignment'; import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values'; -import { isDataLayer, isReferenceLayer } from './visualization_helpers'; +import { isDataLayer } from './visualization_helpers'; export const getSortedAccessors = (datasource: DatasourcePublicAPI, layer: XYLayerConfig) => { const originalOrder = datasource @@ -299,100 +304,140 @@ export const buildExpression = ( hideEndzones: [state?.hideEndzones || false], valuesInLegend: [state?.valuesInLegend || false], layers: validLayers.map((layer) => { - const columnToLabel = getColumnToLabelMap(layer, datasourceLayers[layer.layerId]); + if (isDataLayer(layer)) { + return dataLayerToExpression( + layer, + datasourceLayers[layer.layerId], + metadata, + paletteService + ); + } + return referenceLineLayerToExpression( + layer, + datasourceLayers[(layer as XYReferenceLineLayerConfig).layerId] + ); + }), + }, + }, + ], + }; +}; - const xAxisOperation = - datasourceLayers && - datasourceLayers[layer.layerId].getOperationForColumnId(layer.xAccessor); +const referenceLineLayerToExpression = ( + layer: XYReferenceLineLayerConfig, + datasourceLayer: DatasourcePublicAPI +): Ast => { + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'lens_xy_referenceLine_layer', + arguments: { + layerId: [layer.layerId], + yConfig: layer.yConfig + ? layer.yConfig.map((yConfig) => + yConfigToExpression(yConfig, defaultReferenceLineColor) + ) + : [], + layerType: [layerTypes.REFERENCELINE], + accessors: layer.accessors, + columnToLabel: [JSON.stringify(getColumnToLabelMap(layer, datasourceLayer))], + }, + }, + ], + }; +}; - const isHistogramDimension = Boolean( - xAxisOperation && - xAxisOperation.isBucketed && - xAxisOperation.scale && - xAxisOperation.scale !== 'ordinal' - ); +const dataLayerToExpression = ( + layer: ValidLayer, + datasourceLayer: DatasourcePublicAPI, + metadata: Record>, + paletteService: PaletteRegistry +): Ast => { + const columnToLabel = getColumnToLabelMap(layer, datasourceLayer); - return { - type: 'expression', - chain: [ - { - type: 'function', - function: 'lens_xy_layer', - arguments: { - layerId: [layer.layerId], + const xAxisOperation = datasourceLayer?.getOperationForColumnId(layer.xAccessor); - hide: [Boolean(layer.hide)], + const isHistogramDimension = Boolean( + xAxisOperation && + xAxisOperation.isBucketed && + xAxisOperation.scale && + xAxisOperation.scale !== 'ordinal' + ); - xAccessor: layer.xAccessor ? [layer.xAccessor] : [], - yScaleType: [ - getScaleType(metadata[layer.layerId][layer.accessors[0]], ScaleType.Ordinal), - ], - xScaleType: [ - getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear), - ], - isHistogram: [isHistogramDimension], - splitAccessor: layer.splitAccessor ? [layer.splitAccessor] : [], - yConfig: layer.yConfig - ? layer.yConfig.map((yConfig) => ({ - type: 'expression', - chain: [ - { - type: 'function', - function: 'lens_xy_yConfig', - arguments: { - forAccessor: [yConfig.forAccessor], - axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], - color: isReferenceLayer(layer) - ? [yConfig.color || defaultReferenceLineColor] - : yConfig.color - ? [yConfig.color] - : [], - lineStyle: [yConfig.lineStyle || 'solid'], - lineWidth: [yConfig.lineWidth || 1], - fill: [yConfig.fill || 'none'], - icon: hasIcon(yConfig.icon) ? [yConfig.icon] : [], - iconPosition: - hasIcon(yConfig.icon) || yConfig.textVisibility - ? [yConfig.iconPosition || 'auto'] - : ['auto'], - textVisibility: [yConfig.textVisibility || false], - }, - }, - ], - })) - : [], - seriesType: [layer.seriesType], - layerType: [layer.layerType || layerTypes.DATA], - accessors: layer.accessors, - columnToLabel: [JSON.stringify(columnToLabel)], - ...(layer.palette - ? { - palette: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'theme', - arguments: { - variable: ['palette'], - default: [ - paletteService - .get(layer.palette.name) - .toExpression(layer.palette.params), - ], - }, - }, - ], - }, + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'lens_xy_data_layer', + arguments: { + layerId: [layer.layerId], + hide: [Boolean(layer.hide)], + xAccessor: layer.xAccessor ? [layer.xAccessor] : [], + yScaleType: [ + getScaleType(metadata[layer.layerId][layer.accessors[0]], ScaleType.Ordinal), + ], + xScaleType: [getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear)], + isHistogram: [isHistogramDimension], + splitAccessor: layer.splitAccessor ? [layer.splitAccessor] : [], + yConfig: layer.yConfig + ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig)) + : [], + seriesType: [layer.seriesType], + layerType: [layerTypes.DATA], + accessors: layer.accessors, + columnToLabel: [JSON.stringify(columnToLabel)], + ...(layer.palette + ? { + palette: [ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'theme', + arguments: { + variable: ['palette'], + default: [ + paletteService + .get(layer.palette.name) + .toExpression(layer.palette.params), ], - } - : {}), + }, + }, + ], }, - }, - ], - }; - }), + ], + } + : {}), + }, + }, + ], + }; +}; + +const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => { + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'lens_xy_yConfig', + arguments: { + forAccessor: [yConfig.forAccessor], + axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], + color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], + lineStyle: [yConfig.lineStyle || 'solid'], + lineWidth: [yConfig.lineWidth || 1], + fill: [yConfig.fill || 'none'], + icon: hasIcon(yConfig.icon) ? [yConfig.icon] : [], + iconPosition: + hasIcon(yConfig.icon) || yConfig.textVisibility + ? [yConfig.iconPosition || 'auto'] + : ['auto'], + textVisibility: [yConfig.textVisibility || false], }, }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 51cf15c292647..5e1748a6dc313 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -9,7 +9,7 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; import { Operation, VisualizeEditorContext, Suggestion } from '../types'; import type { State, XYSuggestion } from './types'; -import type { SeriesType, XYLayerConfig } from '../../common/expressions'; +import type { SeriesType, XYDataLayerConfig, XYLayerConfig } from '../../common/expressions'; import { layerTypes } from '../../common'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { LensIconChartBar } from '../assets/chart_bar'; @@ -32,7 +32,7 @@ function exampleState(): State { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - }, + } as XYDataLayerConfig, ], }; } @@ -105,7 +105,7 @@ describe('xy_visualization', () => { return { ...state, layers: types.map((t, i) => ({ - ...state.layers[0], + ...(state.layers[0] as XYDataLayerConfig), layerId: `layer_${i}`, seriesType: t, })), @@ -143,7 +143,7 @@ describe('xy_visualization', () => { const initialState = xyVisualization.initialize(() => 'l1'); expect(initialState.layers).toHaveLength(1); - expect(initialState.layers[0].xAccessor).not.toBeDefined(); + expect((initialState.layers[0] as XYDataLayerConfig).xAccessor).not.toBeDefined(); expect(initialState.layers[0].accessors).toHaveLength(0); expect(initialState).toMatchInlineSnapshot(` @@ -333,7 +333,6 @@ describe('xy_visualization', () => { { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, - seriesType: 'line', accessors: [], }, ], @@ -345,7 +344,6 @@ describe('xy_visualization', () => { ).toEqual({ layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, - seriesType: 'line', accessors: ['newCol'], yConfig: [ { @@ -432,7 +430,7 @@ describe('xy_visualization', () => { }, ]); - expect(state?.layers[0].palette).toStrictEqual({ + expect((state?.layers[0] as XYDataLayerConfig).palette).toStrictEqual({ name: 'temperature', type: 'palette', }); @@ -787,7 +785,11 @@ describe('xy_visualization', () => { state: { ...baseState, layers: [ - { ...baseState.layers[0], accessors: ['a'], seriesType: 'bar_percentage_stacked' }, + { + ...baseState.layers[0], + accessors: ['a'], + seriesType: 'bar_percentage_stacked', + } as XYDataLayerConfig, ], }, frame, @@ -1010,7 +1012,6 @@ describe('xy_visualization', () => { { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, - seriesType: 'line', accessors: [], yConfig: [{ axisMode: 'left', forAccessor: 'a' }], }, @@ -1073,8 +1074,8 @@ describe('xy_visualization', () => { it('should compute no groups for referenceLines when the only data accessor available is a date histogram', () => { const state = getStateWithBaseReferenceLine(); - state.layers[0].xAccessor = 'b'; - state.layers[0].accessors = []; + (state.layers[0] as XYDataLayerConfig).xAccessor = 'b'; + (state.layers[0] as XYDataLayerConfig).accessors = []; state.layers[1].yConfig = []; // empty the configuration // set the xAccessor as date_histogram frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => { @@ -1100,8 +1101,8 @@ describe('xy_visualization', () => { it('should mark horizontal group is invalid when xAccessor is changed to a date histogram', () => { const state = getStateWithBaseReferenceLine(); - state.layers[0].xAccessor = 'b'; - state.layers[0].accessors = []; + (state.layers[0] as XYDataLayerConfig).xAccessor = 'b'; + (state.layers[0] as XYDataLayerConfig).accessors = []; state.layers[1].yConfig![0].axisMode = 'bottom'; // set the xAccessor as date_histogram frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => { @@ -1132,10 +1133,10 @@ describe('xy_visualization', () => { it('should return groups in a specific order (left, right, bottom)', () => { const state = getStateWithBaseReferenceLine(); - state.layers[0].xAccessor = 'c'; - state.layers[0].accessors = ['a', 'b']; + (state.layers[0] as XYDataLayerConfig).xAccessor = 'c'; + (state.layers[0] as XYDataLayerConfig).accessors = ['a', 'b']; // invert them on purpose - state.layers[0].yConfig = [ + (state.layers[0] as XYDataLayerConfig).yConfig = [ { axisMode: 'right', forAccessor: 'b' }, { axisMode: 'left', forAccessor: 'a' }, ]; @@ -1170,8 +1171,8 @@ describe('xy_visualization', () => { it('should ignore terms operation for xAccessor', () => { const state = getStateWithBaseReferenceLine(); - state.layers[0].xAccessor = 'b'; - state.layers[0].accessors = []; + (state.layers[0] as XYDataLayerConfig).xAccessor = 'b'; + (state.layers[0] as XYDataLayerConfig).accessors = []; state.layers[1].yConfig = []; // empty the configuration // set the xAccessor as top values frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => { @@ -1197,8 +1198,8 @@ describe('xy_visualization', () => { it('should mark horizontal group is invalid when accessor is changed to a terms operation', () => { const state = getStateWithBaseReferenceLine(); - state.layers[0].xAccessor = 'b'; - state.layers[0].accessors = []; + (state.layers[0] as XYDataLayerConfig).xAccessor = 'b'; + (state.layers[0] as XYDataLayerConfig).accessors = []; state.layers[1].yConfig![0].axisMode = 'bottom'; // set the xAccessor as date_histogram frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => { @@ -1262,7 +1263,7 @@ describe('xy_visualization', () => { }; const state = getStateWithBaseReferenceLine(); - state.layers[0].accessors = ['yAccessorId', 'yAccessorId2']; + (state.layers[0] as XYDataLayerConfig).accessors = ['yAccessorId', 'yAccessorId2']; state.layers[1].yConfig = []; // empty the configuration const options = xyVisualization.getConfiguration({ @@ -1282,8 +1283,12 @@ describe('xy_visualization', () => { it('should be excluded and not crash when a custom palette is used for data layer', () => { const state = getStateWithBaseReferenceLine(); // now add a breakdown on the data layer with a custom palette - state.layers[0].palette = { type: 'palette', name: 'custom', params: {} }; - state.layers[0].splitAccessor = 'd'; + (state.layers[0] as XYDataLayerConfig).palette = { + type: 'palette', + name: 'custom', + params: {}, + }; + (state.layers[0] as XYDataLayerConfig).splitAccessor = 'd'; const options = xyVisualization.getConfiguration({ state, @@ -1306,7 +1311,7 @@ describe('xy_visualization', () => { ...baseState.layers[0], splitAccessor: undefined, ...layerConfigOverride, - }, + } as XYDataLayerConfig, ], }, frame, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 54fc4e0594a7f..33b285bd19ea3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { groupBy, uniq } from 'lodash'; import { render } from 'react-dom'; import { Position } from '@elastic/charts'; import { FormattedMessage, I18nProvider } from '@kbn/i18n-react'; @@ -22,7 +21,7 @@ import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { LayerHeader } from './xy_config_panel/layer_header'; import type { Visualization, AccessorConfig, FramePublicAPI } from '../types'; import { State, visualizationTypes, XYSuggestion } from './types'; -import { SeriesType, XYLayerConfig, YAxisMode } from '../../common/expressions'; +import { SeriesType, XYDataLayerConfig, XYLayerConfig, YAxisMode } from '../../common/expressions'; import { layerTypes } from '../../common'; import { isHorizontalChart } from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; @@ -38,7 +37,9 @@ import { checkXAccessorCompatibility, defaultSeriesType, getAxisName, + getDataLayers, getDescription, + getFirstDataLayer, getLayersByType, getVisualizationType, isBucketed, @@ -87,16 +88,12 @@ export const getXyVisualization = ({ }, appendLayer(state, layerId, layerType) { - const usedSeriesTypes = uniq(state.layers.map((layer) => layer.seriesType)); + const firstUsedSeriesType = getDataLayers(state.layers)?.[0]?.seriesType; return { ...state, layers: [ ...state.layers, - newLayerState( - usedSeriesTypes.length === 1 ? usedSeriesTypes[0] : state.preferredSeriesType, - layerId, - layerType - ), + newLayerState(firstUsedSeriesType || state.preferredSeriesType, layerId, layerType), ], }; }, @@ -175,6 +172,7 @@ export const getXyVisualization = ({ if (isReferenceLayer(layer)) { return getReferenceConfiguration({ state, frame, layer, sortedAccessors, mappedAccessors }); } + const dataLayers = getDataLayers(state.layers); const isHorizontal = isHorizontalChart(state.layers); const { left, right } = groupAxesByType([layer], frame.activeData); @@ -185,24 +183,21 @@ export const getXyVisualization = ({ (right.length && right.length < 2) ); // Check also for multiple layers that can stack for percentage charts - // Make sure that if multiple dimensions are defined for a single layer, they should belong to the same axis + // Make sure that if multiple dimensions are defined for a single dataLayer, they should belong to the same axis const hasOnlyOneAccessor = layerHasOnlyOneAccessor && - getLayersByType(state, layerTypes.DATA).filter( + dataLayers.filter( // check that the other layers are compatible with this one - (dataLayer) => { + (l) => { if ( - dataLayer.seriesType === layer.seriesType && - Boolean(dataLayer.xAccessor) === Boolean(layer.xAccessor) && - Boolean(dataLayer.splitAccessor) === Boolean(layer.splitAccessor) + l.seriesType === layer.seriesType && + Boolean(l.xAccessor) === Boolean(layer.xAccessor) && + Boolean(l.splitAccessor) === Boolean(layer.splitAccessor) ) { - const { left: localLeft, right: localRight } = groupAxesByType( - [dataLayer], - frame.activeData - ); + const { left: localLeft, right: localRight } = groupAxesByType([l], frame.activeData); // return true only if matching axis are found return ( - dataLayer.accessors.length && + l.accessors.length && (Boolean(localLeft.length) === Boolean(left.length) || Boolean(localRight.length) === Boolean(right.length)) ); @@ -259,7 +254,7 @@ export const getXyVisualization = ({ getMainPalette: (state) => { if (!state || state.layers.length === 0) return; - return state.layers[0].palette; + return getFirstDataLayer(state.layers)?.palette; }, setDimension(props) { @@ -371,14 +366,18 @@ export const getXyVisualization = ({ if (!foundLayer) { return prevState; } + const dataLayers = getDataLayers(prevState.layers); const newLayer = { ...foundLayer }; - if (newLayer.xAccessor === columnId) { - delete newLayer.xAccessor; - } else if (newLayer.splitAccessor === columnId) { - delete newLayer.splitAccessor; - // as the palette is associated with the break down by dimension, remove it together with the dimension - delete newLayer.palette; - } else if (newLayer.accessors.includes(columnId)) { + if (isDataLayer(newLayer)) { + if (newLayer.xAccessor === columnId) { + delete newLayer.xAccessor; + } else if (newLayer.splitAccessor === columnId) { + delete newLayer.splitAccessor; + // as the palette is associated with the break down by dimension, remove it together with the dimension + delete newLayer.palette; + } + } + if (newLayer.accessors.includes(columnId)) { newLayer.accessors = newLayer.accessors.filter((a) => a !== columnId); } @@ -388,10 +387,9 @@ export const getXyVisualization = ({ let newLayers = prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)); // check if there's any reference layer and pull it off if all data layers have no dimensions set - const layersByType = groupBy(newLayers, ({ layerType }) => layerType); // check for data layers if they all still have xAccessors const groupsAvailable = getGroupsAvailableInData( - layersByType[layerTypes.DATA], + dataLayers, frame.datasourceLayers, frame?.activeData ); @@ -453,9 +451,11 @@ export const getXyVisualization = ({ getErrorMessages(state, datasourceLayers) { // Data error handling below here - const hasNoAccessors = ({ accessors }: XYLayerConfig) => + const hasNoAccessors = ({ accessors }: XYDataLayerConfig) => accessors == null || accessors.length === 0; - const hasNoSplitAccessor = ({ splitAccessor, seriesType }: XYLayerConfig) => + + const dataLayers = getDataLayers(state.layers); + const hasNoSplitAccessor = ({ splitAccessor, seriesType }: XYDataLayerConfig) => seriesType.includes('percentage') && splitAccessor == null; const errors: Array<{ @@ -466,16 +466,15 @@ export const getXyVisualization = ({ // check if the layers in the state are compatible with this type of chart if (state && state.layers.length > 1) { // Order is important here: Y Axis is fundamental to exist to make it valid - const checks: Array<[string, (layer: XYLayerConfig) => boolean]> = [ + const checks: Array<[string, (layer: XYDataLayerConfig) => boolean]> = [ ['Y', hasNoAccessors], ['Break down', hasNoSplitAccessor], ]; // filter out those layers with no accessors at all - const filteredLayers = state.layers.filter( - ({ accessors, xAccessor, splitAccessor, layerType }: XYLayerConfig) => - isDataLayer({ layerType }) && - (accessors.length > 0 || xAccessor != null || splitAccessor != null) + const filteredLayers = dataLayers.filter( + ({ accessors, xAccessor, splitAccessor, layerType }) => + accessors.length > 0 || xAccessor != null || splitAccessor != null ); for (const [dimension, criteria] of checks) { const result = validateLayersForDimension(dimension, filteredLayers, criteria); diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index 6031ed7223039..69df0d80300b2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -10,7 +10,12 @@ import { uniq } from 'lodash'; import { DatasourcePublicAPI, OperationMetadata, VisualizationType } from '../types'; import { State, visualizationTypes, XYState } from './types'; import { isHorizontalChart } from './state_helpers'; -import { SeriesType, XYLayerConfig } from '../../common/expressions'; +import { + SeriesType, + XYDataLayerConfig, + XYLayerConfig, + XYReferenceLineLayerConfig, +} from '../../common/expressions'; import { layerTypes } from '..'; import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; import { LensIconChartMixedXy } from '../assets/chart_mixed_xy'; @@ -59,14 +64,15 @@ export function checkXAccessorCompatibility( state: XYState, datasourceLayers: Record ) { + const dataLayers = getDataLayers(state.layers); const errors = []; - const hasDateHistogramSet = state.layers.some( + const hasDateHistogramSet = dataLayers.some( checkScaleOperation('interval', 'date', datasourceLayers) ); - const hasNumberHistogram = state.layers.some( + const hasNumberHistogram = dataLayers.some( checkScaleOperation('interval', 'number', datasourceLayers) ); - const hasOrdinalAxis = state.layers.some( + const hasOrdinalAxis = dataLayers.some( checkScaleOperation('ordinal', undefined, datasourceLayers) ); if (state.layers.length > 1 && hasDateHistogramSet && hasNumberHistogram) { @@ -109,7 +115,7 @@ export function checkScaleOperation( dataType: 'date' | 'number' | 'string' | undefined, datasourceLayers: Record ) { - return (layer: XYLayerConfig) => { + return (layer: XYDataLayerConfig) => { const datasourceAPI = datasourceLayers[layer.layerId]; if (!layer.xAccessor) { return false; @@ -121,11 +127,20 @@ export function checkScaleOperation( }; } -export const isDataLayer = (layer: Pick) => - layer.layerType === layerTypes.DATA; +export const isDataLayer = (layer: Pick): layer is XYDataLayerConfig => + layer.layerType === layerTypes.DATA || !layer.layerType; -export const isReferenceLayer = (layer: Pick) => - layer?.layerType === layerTypes.REFERENCELINE; +export const getDataLayers = (layers: XYLayerConfig[]) => + (layers || []).filter((layer): layer is XYDataLayerConfig => isDataLayer(layer)); + +export const getFirstDataLayer = (layers: XYLayerConfig[]) => + (layers || []).find((layer): layer is XYDataLayerConfig => isDataLayer(layer)); + +export const isReferenceLayer = (layer: XYLayerConfig): layer is XYReferenceLineLayerConfig => + layer.layerType === layerTypes.REFERENCELINE; + +export const getReferenceLayers = (layers: XYLayerConfig[]) => + (layers || []).filter((layer): layer is XYReferenceLineLayerConfig => isReferenceLayer(layer)); export function getVisualizationType(state: State): VisualizationType | 'mixed' { if (!state.layers.length) { @@ -133,8 +148,9 @@ export function getVisualizationType(state: State): VisualizationType | 'mixed' visualizationTypes.find((t) => t.id === state.preferredSeriesType) ?? visualizationTypes[0] ); } - const visualizationType = visualizationTypes.find((t) => t.id === state.layers[0].seriesType); - const seriesTypes = uniq(state.layers.map((l) => l.seriesType)); + const dataLayers = getDataLayers(state?.layers); + const visualizationType = visualizationTypes.find((t) => t.id === dataLayers?.[0].seriesType); + const seriesTypes = uniq(dataLayers.map((l) => l.seriesType)); return visualizationType && seriesTypes.length === 1 ? visualizationType : 'mixed'; } @@ -241,8 +257,8 @@ export function getLayersByType(state: State, byType?: string) { export function validateLayersForDimension( dimension: string, - layers: XYLayerConfig[], - missingCriteria: (layer: XYLayerConfig) => boolean + layers: XYDataLayerConfig[], + missingCriteria: (layer: XYDataLayerConfig) => boolean ): | { valid: true } | { diff --git a/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx b/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx index 81037418a8143..eb9de9a2993b4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx @@ -10,7 +10,7 @@ import React from 'react'; import moment from 'moment'; import { Endzones } from '../../../../../src/plugins/charts/public'; import type { LensMultiTable } from '../../common'; -import type { LayerArgs } from '../../common/expressions'; +import type { DataLayerArgs } from '../../common/expressions'; import { search } from '../../../../../src/plugins/data/public'; export interface XDomain { @@ -19,7 +19,7 @@ export interface XDomain { minInterval?: number; } -export const getAppliedTimeRange = (layers: LayerArgs[], data: LensMultiTable) => { +export const getAppliedTimeRange = (layers: DataLayerArgs[], data: LensMultiTable) => { return Object.entries(data.tables) .map(([tableId, table]) => { const layer = layers.find((l) => l.layerId === tableId); @@ -37,7 +37,7 @@ export const getAppliedTimeRange = (layers: LayerArgs[], data: LensMultiTable) = }; export const getXDomain = ( - layers: LayerArgs[], + layers: DataLayerArgs[], data: LensMultiTable, minInterval: number | undefined, isTimeViz: boolean, diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index c59edfeab0fb8..5db92e2dbb568 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -22,7 +22,7 @@ import { import { getSortedAccessors } from '../to_expression'; import { updateLayer } from '.'; import { TooltipWrapper } from '../../shared_components'; -import { isReferenceLayer } from '../visualization_helpers'; +import { isDataLayer, isReferenceLayer } from '../visualization_helpers'; const tooltipContent = { auto: i18n.translate('xpack.lens.configPanel.color.tooltip.auto', { @@ -55,7 +55,6 @@ export const ColorPicker = ({ }) => { const index = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[index]; - const disabled = Boolean(layer.splitAccessor); const overwriteColor = getSeriesColor(layer, accessor); const currentColor = useMemo(() => { @@ -87,6 +86,7 @@ export const ColorPicker = ({ const [color, setColor] = useState(currentColor); + const disabled = Boolean(isDataLayer(layer) && layer.splitAccessor); const handleColor: EuiColorPickerProps['onChange'] = (text, output) => { setColor(text); if (output.isValid || text === '') { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx index 94df921ba1e5d..240b85a74eb2e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx @@ -20,6 +20,7 @@ import { VisualOptionsPopover } from './visual_options_popover'; import { getScaleType } from '../to_expression'; import { TooltipWrapper } from '../../shared_components'; import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; +import { getDataLayers } from '../visualization_helpers'; type UnwrapArray = T extends Array ? P : T; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; @@ -103,7 +104,7 @@ function hasPercentageAxis(axisGroups: GroupsConfiguration, groupId: string, sta axisGroups .find((group) => group.groupId === groupId) ?.series.some(({ layer: layerId }) => - state?.layers.find( + getDataLayers(state?.layers).find( (layer) => layer.layerId === layerId && layer.seriesType.includes('percentage') ) ) @@ -115,8 +116,9 @@ export const XyToolbar = memo(function XyToolbar( ) { const { state, setState, frame, useLegacyTimeAxis } = props; + const dataLayers = getDataLayers(state?.layers); const shouldRotate = state?.layers.length ? isHorizontalChart(state.layers) : false; - const axisGroups = getAxesConfiguration(state?.layers, shouldRotate, frame.activeData); + const axisGroups = getAxesConfiguration(dataLayers, shouldRotate, frame.activeData); const dataBounds = getDataBounds(frame.activeData, axisGroups); const tickLabelsVisibilitySettings = { @@ -196,7 +198,7 @@ export const XyToolbar = memo(function XyToolbar( }); }; - const nonOrdinalXAxis = state?.layers.every( + const nonOrdinalXAxis = dataLayers.every( (layer) => !layer.xAccessor || getScaleType( @@ -206,7 +208,7 @@ export const XyToolbar = memo(function XyToolbar( ); // only allow changing endzone visibility if it could show up theoretically (if it's a time viz) - const onChangeEndzoneVisiblity = state?.layers.every( + const onChangeEndzoneVisiblity = dataLayers.every( (layer) => layer.xAccessor && getScaleType( @@ -232,7 +234,7 @@ export const XyToolbar = memo(function XyToolbar( axisGroups .find((group) => group.groupId === 'left') ?.series?.some((series) => { - const seriesType = state.layers.find((l) => l.layerId === series.layer)?.seriesType; + const seriesType = dataLayers.find((l) => l.layerId === series.layer)?.seriesType; return seriesType?.includes('bar') || seriesType?.includes('area'); }) ); @@ -249,7 +251,7 @@ export const XyToolbar = memo(function XyToolbar( axisGroups .find((group) => group.groupId === 'right') ?.series?.some((series) => { - const seriesType = state.layers.find((l) => l.layerId === series.layer)?.seriesType; + const seriesType = dataLayers.find((l) => l.layerId === series.layer)?.seriesType; return seriesType?.includes('bar') || seriesType?.includes('area'); }) ); @@ -263,12 +265,12 @@ export const XyToolbar = memo(function XyToolbar( [setState, state] ); - const filteredBarLayers = state?.layers.filter((layer) => layer.seriesType.includes('bar')); + const filteredBarLayers = dataLayers.filter((layer) => layer.seriesType.includes('bar')); const chartHasMoreThanOneBarSeries = filteredBarLayers.length > 1 || filteredBarLayers.some((layer) => layer.accessors.length > 1 || layer.splitAccessor); - const isTimeHistogramModeEnabled = state?.layers.some( + const isTimeHistogramModeEnabled = dataLayers.some( ({ xAccessor, layerId, seriesType, splitAccessor }) => { if (!xAccessor) { return false; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx index d2f54af8cf21a..465a627fa33b2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui'; import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types'; import { State, visualizationTypes } from '../types'; -import { SeriesType } from '../../../common/expressions'; +import { SeriesType, XYDataLayerConfig } from '../../../common/expressions'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { StaticHeader } from '../../shared_components'; @@ -45,7 +45,7 @@ function DataLayerHeader(props: VisualizationLayerWidgetProps) { const [isPopoverOpen, setPopoverIsOpen] = useState(false); const { state, layerId } = props; const index = state.layers.findIndex((l) => l.layerId === layerId); - const layer = state.layers[index]; + const layer = state.layers[index] as XYDataLayerConfig; const currentVisType = visualizationTypes.find(({ id }) => id === layer.seriesType)!; const horizontalOnly = isHorizontalChart(state.layers); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx index 8ea6f9ace6320..0436a93be94ee 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx @@ -15,6 +15,7 @@ import { XYState } from '../../types'; import { hasHistogramSeries } from '../../state_helpers'; import { ValidLayer } from '../../../../common/expressions'; import type { FramePublicAPI } from '../../../types'; +import { getDataLayers } from '../../visualization_helpers'; function getValueLabelDisableReason({ isAreaPercentage, @@ -49,24 +50,25 @@ export const VisualOptionsPopover: React.FC = ({ setState, datasourceLayers, }) => { - const isAreaPercentage = state?.layers.some( + const dataLayers = getDataLayers(state.layers); + const isAreaPercentage = dataLayers.some( ({ seriesType }) => seriesType === 'area_percentage_stacked' ); - const hasNonBarSeries = state?.layers.some(({ seriesType }) => + const hasNonBarSeries = dataLayers.some(({ seriesType }) => ['area_stacked', 'area', 'line'].includes(seriesType) ); - const hasBarNotStacked = state?.layers.some(({ seriesType }) => + const hasBarNotStacked = dataLayers.some(({ seriesType }) => ['bar', 'bar_horizontal'].includes(seriesType) ); - const hasAreaSeries = state?.layers.some(({ seriesType }) => + const hasAreaSeries = dataLayers.some(({ seriesType }) => ['area_stacked', 'area', 'area_percentage_stacked'].includes(seriesType) ); const isHistogramSeries = Boolean( - hasHistogramSeries(state?.layers as ValidLayer[], datasourceLayers) + hasHistogramSeries(dataLayers as ValidLayer[], datasourceLayers) ); const isValueLabelsEnabled = !hasNonBarSeries && hasBarNotStacked && !isHistogramSeries; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx index fe8a71ad9f326..4e2be2f0e4749 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx @@ -16,6 +16,7 @@ import { ToolbarPopover, ValueLabelsSettings } from '../../../shared_components' import { MissingValuesOptions } from './missing_values_option'; import { FillOpacityOption } from './fill_opacity_option'; import { layerTypes } from '../../../../common'; +import { XYDataLayerConfig } from '../../../../common/expressions'; describe('Visual options popover', () => { let frame: FramePublicAPI; @@ -52,7 +53,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_stacked' }], + layers: [{ ...state.layers[0], seriesType: 'bar_stacked' } as XYDataLayerConfig], }} /> ); @@ -68,7 +69,9 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' }], + layers: [ + { ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig, + ], }} /> ); @@ -85,7 +88,9 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' }], + layers: [ + { ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig, + ], }} /> ); @@ -101,7 +106,9 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' }], + layers: [ + { ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig, + ], }} /> ); @@ -138,7 +145,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' }], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], fittingFunction: 'Carry', }} /> @@ -155,7 +162,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' }], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], fittingFunction: 'Carry', }} /> @@ -172,7 +179,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'line' }], + layers: [{ ...state.layers[0], seriesType: 'line' } as XYDataLayerConfig], fittingFunction: 'Carry', }} /> @@ -190,7 +197,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' }], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], fittingFunction: 'Carry', }} /> @@ -207,7 +214,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'area' }], + layers: [{ ...state.layers[0], seriesType: 'area' } as XYDataLayerConfig], fittingFunction: 'Carry', }} /> @@ -230,7 +237,7 @@ describe('Visual options popover', () => { state={{ ...state, layers: [ - { ...state.layers[0], seriesType: 'bar' }, + { ...state.layers[0], seriesType: 'bar' } as XYDataLayerConfig, { seriesType: 'bar', layerType: layerTypes.DATA, diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index 5dea4481a60e5..1e80c6e843ba2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -18,6 +18,7 @@ import { createMockFramePublicAPI, createMockDatasource } from '../../mocks'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { EuiColorPicker } from '@elastic/eui'; import { layerTypes } from '../../../common'; +import { XYDataLayerConfig } from '../../../common/expressions'; describe('XY Config panels', () => { let frame: FramePublicAPI; @@ -205,7 +206,10 @@ describe('XY Config panels', () => { setState={jest.fn()} accessor="bar" groupId="left" - state={{ ...state, layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' }] }} + state={{ + ...state, + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], + }} formatFactory={jest.fn()} paletteService={chartPluginMock.createPaletteRegistry()} panelRef={React.createRef()} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts index 89276d00b5ed0..679f3537fdd6a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts @@ -15,6 +15,7 @@ import { PaletteOutput } from 'src/plugins/charts/public'; import { layerTypes } from '../../common'; import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; import { themeServiceMock } from '../../../../../src/core/public/mocks'; +import { XYDataLayerConfig } from '../../common/expressions'; jest.mock('../id_generator'); @@ -89,12 +90,14 @@ describe('xy_suggestions', () => { // Helper that plucks out the important part of a suggestion for // most test assertions function suggestionSubset(suggestion: VisualizationSuggestion) { - return suggestion.state.layers.map(({ seriesType, splitAccessor, xAccessor, accessors }) => ({ - seriesType, - splitAccessor, - x: xAccessor, - y: accessors, - })); + return (suggestion.state.layers as XYDataLayerConfig[]).map( + ({ seriesType, splitAccessor, xAccessor, accessors }) => ({ + seriesType, + splitAccessor, + x: xAccessor, + y: accessors, + }) + ); } beforeEach(() => { @@ -543,7 +546,7 @@ describe('xy_suggestions', () => { mainPalette, }); - expect(suggestion.state.layers[0].palette).toEqual(mainPalette); + expect((suggestion.state.layers as XYDataLayerConfig[])[0].palette).toEqual(mainPalette); }); test('ignores passed in palette for non splitted charts', () => { @@ -559,7 +562,7 @@ describe('xy_suggestions', () => { mainPalette, }); - expect(suggestion.state.layers[0].palette).toEqual(undefined); + expect((suggestion.state.layers as XYDataLayerConfig[])[0].palette).toEqual(undefined); }); test('hides reduced suggestions if there is a current state', () => { @@ -655,7 +658,7 @@ describe('xy_suggestions', () => { expect(suggestions[0].hide).toEqual(false); expect(suggestions[0].state.preferredSeriesType).toEqual('line'); - expect(suggestions[0].state.layers[0].seriesType).toEqual('line'); + expect((suggestions[0].state.layers[0] as XYDataLayerConfig).seriesType).toEqual('line'); }); test('makes a visible seriesType suggestion for unchanged table without split', () => { @@ -779,7 +782,11 @@ describe('xy_suggestions', () => { expect(rest).toHaveLength(visualizationTypes.length - 1); expect(suggestion.state.preferredSeriesType).toEqual('bar_horizontal'); - expect(suggestion.state.layers.every((l) => l.seriesType === 'bar_horizontal')).toBeTruthy(); + expect( + (suggestion.state.layers as XYDataLayerConfig[]).every( + (l) => l.seriesType === 'bar_horizontal' + ) + ).toBeTruthy(); expect(suggestion.title).toEqual('Flip'); }); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index 090c8ccba7984..656c3fa8422e5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -17,9 +17,10 @@ import { TableChangeType, } from '../types'; import { State, XYState, visualizationTypes } from './types'; -import type { SeriesType, XYLayerConfig } from '../../common/expressions'; +import type { SeriesType, XYDataLayerConfig } from '../../common/expressions'; import { layerTypes } from '../../common'; import { getIconForSeries } from './state_helpers'; +import { getDataLayers, isDataLayer } from './visualization_helpers'; const columnSortOrder = { document: 0, @@ -158,7 +159,8 @@ function flipSeriesType(seriesType: SeriesType) { function getBucketMappings(table: TableSuggestion, currentState?: State) { const currentLayer = - currentState && currentState.layers.find(({ layerId }) => layerId === table.layerId); + currentState && + getDataLayers(currentState.layers).find(({ layerId }) => layerId === table.layerId); const buckets = table.columns.filter((col) => col.operation.isBucketed); // reverse the buckets before prioritization to always use the most inner @@ -416,7 +418,7 @@ function getSeriesType( const defaultType = 'bar_stacked'; const oldLayer = getExistingLayer(currentState, layerId); - const oldLayerSeriesType = oldLayer ? oldLayer.seriesType : false; + const oldLayerSeriesType = oldLayer && isDataLayer(oldLayer) ? oldLayer.seriesType : false; const closestSeriesType = oldLayerSeriesType || (currentState && currentState.preferredSeriesType) || defaultType; @@ -496,7 +498,8 @@ function buildSuggestion({ splitBy = xValue; xValue = undefined; } - const existingLayer: XYLayerConfig | {} = getExistingLayer(currentState, layerId) || {}; + const existingLayer: XYDataLayerConfig | {} = + getExistingLayer(currentState, layerId) || ({} as XYDataLayerConfig); const accessors = yValues.map((col) => col.columnId); const newLayer = { ...existingLayer, diff --git a/x-pack/plugins/lens/server/expressions/expressions.ts b/x-pack/plugins/lens/server/expressions/expressions.ts index f258db7f9aede..1af99ac11e5e3 100644 --- a/x-pack/plugins/lens/server/expressions/expressions.ts +++ b/x-pack/plugins/lens/server/expressions/expressions.ts @@ -11,7 +11,8 @@ import { counterRate, metricChart, yAxisConfig, - layerConfig, + dataLayerConfig, + referenceLineLayerConfig, formatColumn, legendConfig, renameColumns, @@ -39,7 +40,8 @@ export const setupExpressions = ( counterRate, metricChart, yAxisConfig, - layerConfig, + dataLayerConfig, + referenceLineLayerConfig, formatColumn, legendConfig, renameColumns, From 0af4578c5f950a5cd5fffde6958d0bb157ae85bf Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Tue, 8 Mar 2022 15:27:44 -0500 Subject: [PATCH 077/140] [ResponseOps] Add new plugin to collect additional kibana monitoring metrics (#123241) * Add new plugint to collect additional kibana monitoring metrics * Readme * Update generated document * We won't use this route * Use dynamic route style * Add in mapping verification * Fix types * Feedback from PR * PR feedback * We do not need this * PR feedback * Match options to api/stats * PR feedback * Ensure we always require auth Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/developer/plugin-list.asciidoc | 4 + .../plugins/monitoring_collection/README.md | 5 + .../monitoring_collection/jest.config.js | 17 +++ .../plugins/monitoring_collection/kibana.json | 15 +++ .../monitoring_collection/server/config.ts | 17 +++ .../monitoring_collection/server/constants.ts | 7 + .../monitoring_collection/server/index.ts | 21 +++ .../server/lib/get_es_cluster_uuid.test.ts | 33 +++++ .../server/lib/get_es_cluster_uuid.ts | 15 +++ .../server/lib/get_kibana_stats.test.ts | 60 +++++++++ .../server/lib/get_kibana_stats.ts | 47 +++++++ .../monitoring_collection/server/lib/index.ts | 9 ++ .../server/plugin.test.ts | 125 ++++++++++++++++++ .../monitoring_collection/server/plugin.ts | 91 +++++++++++++ .../server/routes/dynamic_route.test.ts | 115 ++++++++++++++++ .../server/routes/dynamic_route.ts | 65 +++++++++ .../server/routes/index.ts | 8 ++ .../monitoring_collection/tsconfig.json | 17 +++ 18 files changed, 671 insertions(+) create mode 100644 x-pack/plugins/monitoring_collection/README.md create mode 100644 x-pack/plugins/monitoring_collection/jest.config.js create mode 100644 x-pack/plugins/monitoring_collection/kibana.json create mode 100644 x-pack/plugins/monitoring_collection/server/config.ts create mode 100644 x-pack/plugins/monitoring_collection/server/constants.ts create mode 100644 x-pack/plugins/monitoring_collection/server/index.ts create mode 100644 x-pack/plugins/monitoring_collection/server/lib/get_es_cluster_uuid.test.ts create mode 100644 x-pack/plugins/monitoring_collection/server/lib/get_es_cluster_uuid.ts create mode 100644 x-pack/plugins/monitoring_collection/server/lib/get_kibana_stats.test.ts create mode 100644 x-pack/plugins/monitoring_collection/server/lib/get_kibana_stats.ts create mode 100644 x-pack/plugins/monitoring_collection/server/lib/index.ts create mode 100644 x-pack/plugins/monitoring_collection/server/plugin.test.ts create mode 100644 x-pack/plugins/monitoring_collection/server/plugin.ts create mode 100644 x-pack/plugins/monitoring_collection/server/routes/dynamic_route.test.ts create mode 100644 x-pack/plugins/monitoring_collection/server/routes/dynamic_route.ts create mode 100644 x-pack/plugins/monitoring_collection/server/routes/index.ts create mode 100644 x-pack/plugins/monitoring_collection/tsconfig.json diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index c26a748839daf..33d291ca26950 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -527,6 +527,10 @@ Elastic. |WARNING: Missing README. +|{kib-repo}blob/{branch}/x-pack/plugins/monitoring_collection/README.md[monitoringCollection] +|This plugin allows for other plugins to add data to Kibana stack monitoring documents. + + |{kib-repo}blob/{branch}/x-pack/plugins/observability/README.md[observability] |This plugin provides shared components and services for use across observability solutions, as well as the observability landing page UI. diff --git a/x-pack/plugins/monitoring_collection/README.md b/x-pack/plugins/monitoring_collection/README.md new file mode 100644 index 0000000000000..1f2d2984af886 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/README.md @@ -0,0 +1,5 @@ +# Monitoring Collection + +## Plugin + +This plugin allows for other plugins to add data to Kibana stack monitoring documents. \ No newline at end of file diff --git a/x-pack/plugins/monitoring_collection/jest.config.js b/x-pack/plugins/monitoring_collection/jest.config.js new file mode 100644 index 0000000000000..d2bb0617857d2 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/jest.config.js @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/monitoring_collection'], + coverageDirectory: '/target/kibana-coverage/jest/x-pack/plugins/monitoring_collection', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/monitoring_collection/{common,public,server}/**/*.{js,ts,tsx}', + ], +}; diff --git a/x-pack/plugins/monitoring_collection/kibana.json b/x-pack/plugins/monitoring_collection/kibana.json new file mode 100644 index 0000000000000..d88b7e87861ea --- /dev/null +++ b/x-pack/plugins/monitoring_collection/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "monitoringCollection", + "version": "8.0.0", + "kibanaVersion": "kibana", + "owner": { + "name": "Stack Monitoring", + "githubTeam": "stack-monitoring-ui" + }, + "configPath": ["monitoring_collection"], + "requiredPlugins": [], + "optionalPlugins": [ + ], + "server": true, + "ui": false +} diff --git a/x-pack/plugins/monitoring_collection/server/config.ts b/x-pack/plugins/monitoring_collection/server/config.ts new file mode 100644 index 0000000000000..275d2f31e505d --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/config.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), +}); + +export type MonitoringCollectionConfig = ReturnType; +export function createConfig(config: TypeOf) { + return config; +} diff --git a/x-pack/plugins/monitoring_collection/server/constants.ts b/x-pack/plugins/monitoring_collection/server/constants.ts new file mode 100644 index 0000000000000..90f3ce67ffaeb --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/constants.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export const TYPE_ALLOWLIST = ['rules', 'actions']; diff --git a/x-pack/plugins/monitoring_collection/server/index.ts b/x-pack/plugins/monitoring_collection/server/index.ts new file mode 100644 index 0000000000000..51264a4d8781f --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TypeOf } from '@kbn/config-schema'; +import { PluginInitializerContext, PluginConfigDescriptor } from '../../../../src/core/server'; +import { MonitoringCollectionPlugin } from './plugin'; +import { configSchema } from './config'; + +export type { MonitoringCollectionConfig } from './config'; + +export type { MonitoringCollectionSetup, MetricResult } from './plugin'; + +export const plugin = (initContext: PluginInitializerContext) => + new MonitoringCollectionPlugin(initContext); +export const config: PluginConfigDescriptor> = { + schema: configSchema, +}; diff --git a/x-pack/plugins/monitoring_collection/server/lib/get_es_cluster_uuid.test.ts b/x-pack/plugins/monitoring_collection/server/lib/get_es_cluster_uuid.test.ts new file mode 100644 index 0000000000000..536ecdc076e55 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/lib/get_es_cluster_uuid.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; +import { getESClusterUuid } from '.'; + +describe('getESClusterUuid', () => { + it('should return the result of the es client call', async () => { + const esClientMock = elasticsearchClientMock.createScopedClusterClient(); + // @ts-ignore + esClientMock.asCurrentUser.info.mockImplementation(() => { + return { cluster_uuid: '1abc' }; + }); + const clusterUuid = await getESClusterUuid(esClientMock); + expect(esClientMock.asCurrentUser.info).toHaveBeenCalledWith({ filter_path: 'cluster_uuid' }); + expect(clusterUuid).toBe('1abc'); + }); + + it('should fail gracefully if an error is thrown at the ES level', async () => { + const esClientMock = elasticsearchClientMock.createScopedClusterClient(); + // @ts-ignore + esClientMock.asCurrentUser.info.mockImplementation(() => { + return undefined; + }); + const clusterUuid = await getESClusterUuid(esClientMock); + expect(esClientMock.asCurrentUser.info).toHaveBeenCalledWith({ filter_path: 'cluster_uuid' }); + expect(clusterUuid).toBeUndefined(); + }); +}); diff --git a/x-pack/plugins/monitoring_collection/server/lib/get_es_cluster_uuid.ts b/x-pack/plugins/monitoring_collection/server/lib/get_es_cluster_uuid.ts new file mode 100644 index 0000000000000..cca82537cd9e4 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/lib/get_es_cluster_uuid.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IScopedClusterClient } from '../../../../../src/core/server'; + +export async function getESClusterUuid(client: IScopedClusterClient) { + const response = await client.asCurrentUser.info({ + filter_path: 'cluster_uuid', + }); + return response?.cluster_uuid; +} diff --git a/x-pack/plugins/monitoring_collection/server/lib/get_kibana_stats.test.ts b/x-pack/plugins/monitoring_collection/server/lib/get_kibana_stats.test.ts new file mode 100644 index 0000000000000..7697cfda6d22d --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/lib/get_kibana_stats.test.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ServiceStatusLevels } from '../../../../../src/core/server'; +import { getKibanaStats } from '.'; + +describe('getKibanaStats', () => { + const config = { + allowAnonymous: true, + kibanaIndex: '.kibana', + kibanaVersion: '8.0.0', + uuid: 'abc123', + server: { + name: 'server', + hostname: 'host', + port: 123, + }, + }; + + it('should return the stats', async () => { + const getStatus = () => ({ + level: ServiceStatusLevels.available, + summary: 'Service is working', + }); + const stats = await getKibanaStats({ config, getStatus }); + expect(stats).toStrictEqual({ + uuid: config.uuid, + name: config.server.name, + index: config.kibanaIndex, + host: config.server.hostname, + locale: 'en', + transport_address: `${config.server.hostname}:${config.server.port}`, + version: '8.0.0', + snapshot: false, + status: 'green', + }); + }); + + it('should handle a non green status', async () => { + const getStatus = () => ({ + level: ServiceStatusLevels.critical, + summary: 'Service is NOT working', + }); + const stats = await getKibanaStats({ config, getStatus }); + expect(stats).toStrictEqual({ + uuid: config.uuid, + name: config.server.name, + index: config.kibanaIndex, + host: config.server.hostname, + locale: 'en', + transport_address: `${config.server.hostname}:${config.server.port}`, + version: '8.0.0', + snapshot: false, + status: 'red', + }); + }); +}); diff --git a/x-pack/plugins/monitoring_collection/server/lib/get_kibana_stats.ts b/x-pack/plugins/monitoring_collection/server/lib/get_kibana_stats.ts new file mode 100644 index 0000000000000..7d3011deb447d --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/lib/get_kibana_stats.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import { ServiceStatus, ServiceStatusLevels } from '../../../../../src/core/server'; + +const SNAPSHOT_REGEX = /-snapshot/i; + +const ServiceStatusToLegacyState: Record = { + [ServiceStatusLevels.critical.toString()]: 'red', + [ServiceStatusLevels.unavailable.toString()]: 'red', + [ServiceStatusLevels.degraded.toString()]: 'yellow', + [ServiceStatusLevels.available.toString()]: 'green', +}; + +export function getKibanaStats({ + config, + getStatus, +}: { + config: { + kibanaIndex: string; + kibanaVersion: string; + uuid: string; + server: { + name: string; + hostname: string; + port: number; + }; + }; + getStatus: () => ServiceStatus; +}) { + const status = getStatus(); + return { + uuid: config.uuid, + name: config.server.name, + index: config.kibanaIndex, + host: config.server.hostname, + locale: i18n.getLocale(), + transport_address: `${config.server.hostname}:${config.server.port}`, + version: config.kibanaVersion.replace(SNAPSHOT_REGEX, ''), + snapshot: SNAPSHOT_REGEX.test(config.kibanaVersion), + status: ServiceStatusToLegacyState[status.level.toString()], + }; +} diff --git a/x-pack/plugins/monitoring_collection/server/lib/index.ts b/x-pack/plugins/monitoring_collection/server/lib/index.ts new file mode 100644 index 0000000000000..0c39a62ab359c --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/lib/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getKibanaStats } from './get_kibana_stats'; +export { getESClusterUuid } from './get_es_cluster_uuid'; diff --git a/x-pack/plugins/monitoring_collection/server/plugin.test.ts b/x-pack/plugins/monitoring_collection/server/plugin.test.ts new file mode 100644 index 0000000000000..ebdb33c78322f --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/plugin.test.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { coreMock } from '../../../../src/core/server/mocks'; +import { MonitoringCollectionPlugin } from './plugin'; + +describe('monitoring_collection plugin', () => { + describe('setup()', () => { + let context: ReturnType; + let plugin: MonitoringCollectionPlugin; + let coreSetup: ReturnType; + + beforeEach(() => { + context = coreMock.createPluginInitializerContext(); + plugin = new MonitoringCollectionPlugin(context); + coreSetup = coreMock.createSetup(); + coreSetup.getStartServices = jest.fn().mockResolvedValue([ + { + application: {}, + }, + { triggersActionsUi: {} }, + ]); + }); + + it('should allow registering a collector and getting data from it', async () => { + const { registerMetric } = plugin.setup(coreSetup); + registerMetric<{ name: string }>({ + type: 'actions', + schema: { + name: { + type: 'text', + }, + }, + fetch: async () => { + return [ + { + name: 'foo', + }, + ]; + }, + }); + + const metrics = await plugin.getMetric('actions'); + expect(metrics).toStrictEqual([{ name: 'foo' }]); + }); + + it('should allow registering multiple ollectors and getting data from it', async () => { + const { registerMetric } = plugin.setup(coreSetup); + registerMetric<{ name: string }>({ + type: 'actions', + schema: { + name: { + type: 'text', + }, + }, + fetch: async () => { + return [ + { + name: 'foo', + }, + ]; + }, + }); + registerMetric<{ name: string }>({ + type: 'rules', + schema: { + name: { + type: 'text', + }, + }, + fetch: async () => { + return [ + { + name: 'foo', + }, + { + name: 'bar', + }, + { + name: 'foobar', + }, + ]; + }, + }); + + const metrics = await Promise.all([plugin.getMetric('actions'), plugin.getMetric('rules')]); + expect(metrics).toStrictEqual([ + [{ name: 'foo' }], + [{ name: 'foo' }, { name: 'bar' }, { name: 'foobar' }], + ]); + }); + + it('should NOT allow registering a collector that is not in the allowlist', async () => { + const logger = context.logger.get(); + const { registerMetric } = plugin.setup(coreSetup); + registerMetric<{ name: string }>({ + type: 'test', + schema: { + name: { + type: 'text', + }, + }, + fetch: async () => { + return [ + { + name: 'foo', + }, + ]; + }, + }); + const metrics = await plugin.getMetric('test'); + expect((logger.warn as jest.Mock).mock.calls.length).toBe(2); + expect((logger.warn as jest.Mock).mock.calls[0][0]).toBe( + `Skipping registration of metric type 'test'. This type is not supported in the allowlist.` + ); + expect((logger.warn as jest.Mock).mock.calls[1][0]).toBe( + `Call to 'getMetric' failed because type 'test' does not exist.` + ); + expect(metrics).toBeUndefined(); + }); + }); +}); diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts new file mode 100644 index 0000000000000..a86a7c51b4850 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { JsonObject } from '@kbn/utility-types'; +import { CoreSetup, Plugin, PluginInitializerContext, Logger } from 'kibana/server'; +import { registerDynamicRoute } from './routes'; +import { MakeSchemaFrom } from '../../../../src/plugins/usage_collection/server'; +import { ServiceStatus } from '../../../../src/core/server'; +import { TYPE_ALLOWLIST } from './constants'; + +export interface MonitoringCollectionSetup { + registerMetric: (metric: Metric) => void; +} + +export type MetricResult = T & JsonObject; + +export interface Metric { + type: string; + schema: MakeSchemaFrom; + fetch: () => Promise | Array>>; +} + +export class MonitoringCollectionPlugin implements Plugin { + private readonly initializerContext: PluginInitializerContext; + private readonly logger: Logger; + + private metrics: Record> = {}; + + constructor(initializerContext: PluginInitializerContext) { + this.initializerContext = initializerContext; + this.logger = initializerContext.logger.get(); + } + + async getMetric(type: string) { + if (this.metrics.hasOwnProperty(type)) { + return await this.metrics[type].fetch(); + } + this.logger.warn(`Call to 'getMetric' failed because type '${type}' does not exist.`); + return undefined; + } + + setup(core: CoreSetup) { + const router = core.http.createRouter(); + const kibanaIndex = core.savedObjects.getKibanaIndex(); + + let status: ServiceStatus; + core.status.overall$.subscribe((newStatus) => { + status = newStatus; + }); + + registerDynamicRoute({ + router, + config: { + kibanaIndex, + kibanaVersion: this.initializerContext.env.packageInfo.version, + server: core.http.getServerInfo(), + uuid: this.initializerContext.env.instanceUuid, + }, + getStatus: () => status, + getMetric: async (type: string) => { + return await this.getMetric(type); + }, + }); + + return { + registerMetric: (metric: Metric) => { + if (this.metrics.hasOwnProperty(metric.type)) { + this.logger.warn( + `Skipping registration of metric type '${metric.type}'. This type has already been registered.` + ); + return; + } + if (!TYPE_ALLOWLIST.includes(metric.type)) { + this.logger.warn( + `Skipping registration of metric type '${metric.type}'. This type is not supported in the allowlist.` + ); + return; + } + this.metrics[metric.type] = metric; + }, + }; + } + + start() {} + + stop() {} +} diff --git a/x-pack/plugins/monitoring_collection/server/routes/dynamic_route.test.ts b/x-pack/plugins/monitoring_collection/server/routes/dynamic_route.test.ts new file mode 100644 index 0000000000000..88f0f5b7dc8f0 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/routes/dynamic_route.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { registerDynamicRoute } from './dynamic_route'; +import { + KibanaRequest, + KibanaResponseFactory, + ServiceStatusLevels, +} from '../../../../../src/core/server'; +import { httpServerMock, httpServiceMock } from 'src/core/server/mocks'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; + +beforeEach(() => { + jest.resetAllMocks(); +}); + +jest.mock('../lib', () => ({ + getESClusterUuid: () => 'clusterA', + getKibanaStats: () => ({ name: 'myKibana' }), +})); + +describe('dynamic route', () => { + const kibanaStatsConfig = { + allowAnonymous: true, + kibanaIndex: '.kibana', + kibanaVersion: '8.0.0', + uuid: 'abc123', + server: { + name: 'server', + hostname: 'host', + port: 123, + }, + }; + + const getStatus = () => ({ + level: ServiceStatusLevels.available, + summary: 'Service is working', + }); + + it('returns for a valid type', async () => { + const router = httpServiceMock.createRouter(); + + const getMetric = async () => { + return { foo: 1 }; + }; + registerDynamicRoute({ + router, + config: kibanaStatsConfig, + getStatus, + getMetric, + }); + + const [_, handler] = router.get.mock.calls[0]; + + const esClientMock = elasticsearchClientMock.createScopedClusterClient(); + const context = { + core: { + elasticsearch: { + client: esClientMock, + }, + }, + }; + const req = { params: { type: 'test' } } as KibanaRequest; + const factory: jest.Mocked = httpServerMock.createResponseFactory(); + + await handler(context, req, factory); + + expect(factory.ok).toHaveBeenCalledWith({ + body: { + cluster_uuid: 'clusterA', + kibana: { name: 'myKibana' }, + test: { + foo: 1, + }, + }, + }); + }); + + it('returns the an empty object if there is no data', async () => { + const router = httpServiceMock.createRouter(); + + const getMetric = async () => { + return {}; + }; + registerDynamicRoute({ router, config: kibanaStatsConfig, getStatus, getMetric }); + + const [_, handler] = router.get.mock.calls[0]; + + const esClientMock = elasticsearchClientMock.createScopedClusterClient(); + const context = { + core: { + elasticsearch: { + client: esClientMock, + }, + }, + }; + const req = { params: { type: 'test' } } as KibanaRequest; + const factory: jest.Mocked = httpServerMock.createResponseFactory(); + + await handler(context, req, factory); + + expect(factory.ok).toHaveBeenCalledWith({ + body: { + cluster_uuid: 'clusterA', + kibana: { name: 'myKibana' }, + test: {}, + }, + }); + }); +}); diff --git a/x-pack/plugins/monitoring_collection/server/routes/dynamic_route.ts b/x-pack/plugins/monitoring_collection/server/routes/dynamic_route.ts new file mode 100644 index 0000000000000..d884d8efc15ad --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/routes/dynamic_route.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { JsonObject } from '@kbn/utility-types'; +import { schema } from '@kbn/config-schema'; +import { IRouter, ServiceStatus } from '../../../../../src/core/server'; +import { getESClusterUuid, getKibanaStats } from '../lib'; +import { MetricResult } from '../plugin'; + +export function registerDynamicRoute({ + router, + config, + getStatus, + getMetric, +}: { + router: IRouter; + config: { + kibanaIndex: string; + kibanaVersion: string; + uuid: string; + server: { + name: string; + hostname: string; + port: number; + }; + }; + getStatus: () => ServiceStatus; + getMetric: ( + type: string + ) => Promise> | MetricResult | undefined>; +}) { + router.get( + { + path: `/api/monitoring_collection/{type}`, + options: { + authRequired: true, + tags: ['api'], // ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page + }, + validate: { + params: schema.object({ + type: schema.string(), + }), + }, + }, + async (context, req, res) => { + const type = req.params.type; + const [data, clusterUuid, kibana] = await Promise.all([ + getMetric(type), + getESClusterUuid(context.core.elasticsearch.client), + getKibanaStats({ config, getStatus }), + ]); + + return res.ok({ + body: { + [type]: data, + cluster_uuid: clusterUuid, + kibana, + }, + }); + } + ); +} diff --git a/x-pack/plugins/monitoring_collection/server/routes/index.ts b/x-pack/plugins/monitoring_collection/server/routes/index.ts new file mode 100644 index 0000000000000..eb96ce19f764e --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/routes/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { registerDynamicRoute } from './dynamic_route'; diff --git a/x-pack/plugins/monitoring_collection/tsconfig.json b/x-pack/plugins/monitoring_collection/tsconfig.json new file mode 100644 index 0000000000000..41f781cb8cb9f --- /dev/null +++ b/x-pack/plugins/monitoring_collection/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "server/**/*" + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, + { "path": "../monitoring/tsconfig.json" }, + ] +} From e3e5df66172af7eed9f08e5d5e53c588eec60edf Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Tue, 8 Mar 2022 12:33:08 -0800 Subject: [PATCH 078/140] [Security Solution][Lists] - Disable upload value lists if user is kibana read only (#126829) Addresses #126326 --- .../detection_rules/all_rules_read_only.spec.ts | 7 +++++-- .../detections/pages/detection_engine/rules/index.tsx | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/all_rules_read_only.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/all_rules_read_only.spec.ts index 845c5c2bca9d6..fbb5d9e4b26d9 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/all_rules_read_only.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/all_rules_read_only.spec.ts @@ -12,7 +12,7 @@ import { RULE_CHECKBOX, RULE_NAME, } from '../../screens/alerts_detection_rules'; -import { PAGE_TITLE } from '../../screens/common/page'; +import { VALUE_LISTS_MODAL_ACTIVATOR } from '../../screens/lists'; import { waitForRulesTableToBeLoaded } from '../../tasks/alerts_detection_rules'; import { createCustomRule } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; @@ -35,6 +35,10 @@ describe('All rules - read only', () => { cy.get(RULE_CHECKBOX).should('not.exist'); }); + it('Disables value lists upload', () => { + cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('be.disabled'); + }); + it('Does not display action options', () => { // These are the 3 dots at the end of the row that opens up // options to take action on the rule @@ -51,7 +55,6 @@ describe('All rules - read only', () => { dismissCallOut(MISSING_PRIVILEGES_CALLOUT); cy.reload(); - cy.get(PAGE_TITLE).should('be.visible'); cy.get(RULE_NAME).should('have.text', getNewRule().name); getCallOut(MISSING_PRIVILEGES_CALLOUT).should('not.exist'); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx index b2b80e7945e2d..548e23b438bde 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx @@ -195,7 +195,7 @@ const RulesPageComponent: React.FC = () => { {i18n.UPLOAD_VALUE_LISTS} From daace920d799dd7b36ae52e7af7c226798606986 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 8 Mar 2022 13:11:15 -0800 Subject: [PATCH 079/140] [kbn/pm] add timings for more parts of bootstrap (#127157) --- packages/kbn-pm/dist/index.js | 60 ++++++++++------- packages/kbn-pm/src/commands/bootstrap.ts | 66 +++++++++++-------- scripts/update_vscode_config.js | 2 +- .../browsers/chromium/driver_factory/index.ts | 6 +- 4 files changed, 84 insertions(+), 50 deletions(-) diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 508afa9e9e63a..62bd4e30250b1 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -8908,10 +8908,24 @@ const BootstrapCommand = { const kibanaProjectPath = ((_projects$get = projects.get('kibana')) === null || _projects$get === void 0 ? void 0 : _projects$get.path) || ''; const runOffline = (options === null || options === void 0 ? void 0 : options.offline) === true; const reporter = _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_1__["CiStatsReporter"].fromEnv(_utils_log__WEBPACK_IMPORTED_MODULE_2__["log"]); - const timings = []; // Force install is set in case a flag is passed or + const timings = []; + + const time = async (id, body) => { + const start = Date.now(); + + try { + return await body(); + } finally { + timings.push({ + id, + ms: Date.now() - start + }); + } + }; // Force install is set in case a flag is passed or // if the `.yarn-integrity` file is not found which // will be indicated by the return of yarnIntegrityFileExists. + const forceInstall = !!options && options['force-install'] === true || !(await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["yarnIntegrityFileExists"])(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(kibanaProjectPath, 'node_modules'))); // Ensure we have a `node_modules/.yarn-integrity` file as we depend on it // for bazel to know it has to re-install the node_modules after a reset or a clean @@ -8930,20 +8944,14 @@ const BootstrapCommand = { // if (forceInstall) { - const forceInstallStartTime = Date.now(); - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["runBazel"])(['run', '@nodejs//:yarn'], runOffline); - timings.push({ - id: 'force install dependencies', - ms: Date.now() - forceInstallStartTime + await time('force install dependencies', async () => { + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["runBazel"])(['run', '@nodejs//:yarn'], runOffline); }); } // build packages - const packageStartTime = Date.now(); - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["runBazel"])(['build', '//packages:build', '--show_result=1'], runOffline); - timings.push({ - id: 'build packages', - ms: Date.now() - packageStartTime + await time('build packages', async () => { + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_9__["runBazel"])(['build', '//packages:build', '--show_result=1'], runOffline); }); // Install monorepo npm dependencies outside of the Bazel managed ones for (const batch of batchedNonBazelProjects) { @@ -8965,25 +8973,33 @@ const BootstrapCommand = { } } - await Object(_utils_sort_package_json__WEBPACK_IMPORTED_MODULE_7__["sortPackageJson"])(kbn); - const yarnLock = await Object(_utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__["readYarnLock"])(kbn); + await time('sort package json', async () => { + await Object(_utils_sort_package_json__WEBPACK_IMPORTED_MODULE_7__["sortPackageJson"])(kbn); + }); + const yarnLock = await time('read yarn.lock', async () => await Object(_utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__["readYarnLock"])(kbn)); if (options.validate) { - await Object(_utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_8__["validateDependencies"])(kbn, yarnLock); + await time('validate dependencies', async () => { + await Object(_utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_8__["validateDependencies"])(kbn, yarnLock); + }); } // Assure all kbn projects with bin defined scripts // copy those scripts into the top level node_modules folder // // NOTE: We don't probably need this anymore, is actually not being used - await Object(_utils_link_project_executables__WEBPACK_IMPORTED_MODULE_4__["linkProjectExecutables"])(projects, projectGraph); // Update vscode settings - - await Object(_utils_child_process__WEBPACK_IMPORTED_MODULE_3__["spawnStreaming"])(process.execPath, ['scripts/update_vscode_config'], { - cwd: kbn.getAbsolute(), - env: process.env - }, { - prefix: '[vscode]', - debug: false + await time('link project executables', async () => { + await Object(_utils_link_project_executables__WEBPACK_IMPORTED_MODULE_4__["linkProjectExecutables"])(projects, projectGraph); + }); + await time('update vscode config', async () => { + // Update vscode settings + await Object(_utils_child_process__WEBPACK_IMPORTED_MODULE_3__["spawnStreaming"])(process.execPath, ['scripts/update_vscode_config'], { + cwd: kbn.getAbsolute(), + env: process.env + }, { + prefix: '[vscode]', + debug: false + }); }); // send timings await reporter.timings({ diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index 0b3141ab9a5a9..cf425264ab6d6 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -40,7 +40,19 @@ export const BootstrapCommand: ICommand = { const kibanaProjectPath = projects.get('kibana')?.path || ''; const runOffline = options?.offline === true; const reporter = CiStatsReporter.fromEnv(log); - const timings = []; + + const timings: Array<{ id: string; ms: number }> = []; + const time = async (id: string, body: () => Promise): Promise => { + const start = Date.now(); + try { + return await body(); + } finally { + timings.push({ + id, + ms: Date.now() - start, + }); + } + }; // Force install is set in case a flag is passed or // if the `.yarn-integrity` file is not found which @@ -70,20 +82,14 @@ export const BootstrapCommand: ICommand = { // if (forceInstall) { - const forceInstallStartTime = Date.now(); - await runBazel(['run', '@nodejs//:yarn'], runOffline); - timings.push({ - id: 'force install dependencies', - ms: Date.now() - forceInstallStartTime, + await time('force install dependencies', async () => { + await runBazel(['run', '@nodejs//:yarn'], runOffline); }); } // build packages - const packageStartTime = Date.now(); - await runBazel(['build', '//packages:build', '--show_result=1'], runOffline); - timings.push({ - id: 'build packages', - ms: Date.now() - packageStartTime, + await time('build packages', async () => { + await runBazel(['build', '//packages:build', '--show_result=1'], runOffline); }); // Install monorepo npm dependencies outside of the Bazel managed ones @@ -112,30 +118,38 @@ export const BootstrapCommand: ICommand = { } } - await sortPackageJson(kbn); + await time('sort package json', async () => { + await sortPackageJson(kbn); + }); - const yarnLock = await readYarnLock(kbn); + const yarnLock = await time('read yarn.lock', async () => await readYarnLock(kbn)); if (options.validate) { - await validateDependencies(kbn, yarnLock); + await time('validate dependencies', async () => { + await validateDependencies(kbn, yarnLock); + }); } // Assure all kbn projects with bin defined scripts // copy those scripts into the top level node_modules folder // // NOTE: We don't probably need this anymore, is actually not being used - await linkProjectExecutables(projects, projectGraph); - - // Update vscode settings - await spawnStreaming( - process.execPath, - ['scripts/update_vscode_config'], - { - cwd: kbn.getAbsolute(), - env: process.env, - }, - { prefix: '[vscode]', debug: false } - ); + await time('link project executables', async () => { + await linkProjectExecutables(projects, projectGraph); + }); + + await time('update vscode config', async () => { + // Update vscode settings + await spawnStreaming( + process.execPath, + ['scripts/update_vscode_config'], + { + cwd: kbn.getAbsolute(), + env: process.env, + }, + { prefix: '[vscode]', debug: false } + ); + }); // send timings await reporter.timings({ diff --git a/scripts/update_vscode_config.js b/scripts/update_vscode_config.js index 10ed9fa200b7b..d39e4ce757ce5 100644 --- a/scripts/update_vscode_config.js +++ b/scripts/update_vscode_config.js @@ -6,5 +6,5 @@ * Side Public License, v 1. */ -require('../src/setup_node_env'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/dev-utils').runUpdateVscodeConfigCli(); diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts index 4d1e0566f9943..4664f31f51be4 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts @@ -101,7 +101,7 @@ const DEFAULT_ARGS = [ const DIAGNOSTIC_TIME = 5 * 1000; export class HeadlessChromiumDriverFactory { - private userDataDir = fs.mkdtempSync(path.join(getDataPath(), 'chromium-')); + private userDataDir: string; type = 'chromium'; constructor( @@ -111,6 +111,10 @@ export class HeadlessChromiumDriverFactory { private binaryPath: string, private basePath: string ) { + const dataDir = getDataPath(); + fs.mkdirSync(dataDir, { recursive: true }); + this.userDataDir = fs.mkdtempSync(path.join(dataDir, 'chromium-')); + if (this.config.browser.chromium.disableSandbox) { logger.warn(`Enabling the Chromium sandbox provides an additional layer of protection.`); } From 0410d1f19630d2635986ab4e0f1a8ce2f14349b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Mar 2022 16:12:22 -0500 Subject: [PATCH 080/140] Update dependency chromedriver to v99 (#127079) Co-authored-by: Renovate Bot Co-authored-by: Jonathan Budzenski --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ba4ff174c4c42..19d65e24879b3 100644 --- a/package.json +++ b/package.json @@ -737,7 +737,7 @@ "callsites": "^3.1.0", "chai": "3.5.0", "chance": "1.0.18", - "chromedriver": "^98.0.1", + "chromedriver": "^99.0.0", "clean-webpack-plugin": "^3.0.0", "cmd-shim": "^2.1.0", "compression-webpack-plugin": "^4.0.0", diff --git a/yarn.lock b/yarn.lock index c76e36d61897f..090b6f8cfc0b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9881,10 +9881,10 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chromedriver@^98.0.1: - version "98.0.1" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-98.0.1.tgz#ccb1e36a003b4c6af0b184caa00fca8370d88f2a" - integrity sha512-/04KkHHE/K/lfwdPTQr5fxi1dWvM83p8T/IkYbyGK2PBlH7K49Dd71A9jrS+aWgXlZYkuHhbwiy2PA2QqZ5qQw== +chromedriver@^99.0.0: + version "99.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-99.0.0.tgz#fbfcc7e74991dd50962e7dd456d78eaf49f56774" + integrity sha512-pyB+5LuyZdb7EBPL3i5D5yucZUD+SlkdiUtmpjaEnLd9zAXp+SvD/hP5xF4l/ZmWvUo/1ZLxAI1YBdhazGTpgA== dependencies: "@testim/chrome-version" "^1.1.2" axios "^0.24.0" From 6f5cd00d1fce251ddcddf5808fc8b3a7c638c061 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Tue, 8 Mar 2022 15:20:23 -0600 Subject: [PATCH 081/140] [App Search] Move to tabbed single tabbed JSON flyout with upload and paste options and refactor cards (#127162) * Update flyout components to only serve content and footer We have a new shared flyout coming a future commit called JsonFlyout that will conditionally render the tab content that is now housed in these components. * Update documentation types We no longer have text and file and now have json and elasticsearchIndex * Add logic for tab state for json tab Also changes default value for `creationMode` to `api` since `text` was removed * Add json flyout component * Add stubbed ElasticsearchIndex component To be completed in a future PR * Update DocumentCreationFlyout to render new components * Update DocumentCreationButtons to show new design - Updates the cards to be left rendered instead of a grid - Adds an empty state with illustration for the non-flyout view - Conditionally renders the help text above the buttons in the flyout * Remove unused translations Also renames the ones that were changed * Add missing translation * Fix type error 10:1 error Type export ActiveJsonTab is not a value and should be exported using `export type` @typescript-eslint/consistent-type-exports * Fix typo * Fix duplicate i18n ID Needs to all be in the FormattedMessage component anyway. Original error: https://buildkite.com/elastic/kibana-pull-request/builds/28469#057da46f-28ef-4cdb-8975-8f808757b571 * Add alt text to image --- .../elasticsearch_index.tsx | 115 +++++++++++ .../creation_mode_components/index.ts | 6 +- .../creation_mode_components/json_flyout.scss | 10 + .../json_flyout.test.tsx | 73 +++++++ .../creation_mode_components/json_flyout.tsx | 98 ++++++++++ .../paste_json_text.test.tsx | 42 +--- .../paste_json_text.tsx | 60 ++---- .../show_creation_modes.tsx | 2 +- .../upload_json_file.test.tsx | 44 +---- .../upload_json_file.tsx | 59 ++---- .../document_creation_buttons.test.tsx | 26 ++- .../document_creation_buttons.tsx | 183 ++++++++++++------ .../document_creation_flyout.test.tsx | 19 +- .../document_creation_flyout.tsx | 12 +- .../document_creation_logic.test.ts | 21 +- .../document_creation_logic.ts | 18 +- .../document_creation/illustration.svg | 1 + .../components/document_creation/index.ts | 1 + .../components/document_creation/types.ts | 2 +- .../translations/translations/fr-FR.json | 10 +- .../translations/translations/ja-JP.json | 10 +- .../translations/translations/zh-CN.json | 10 +- 22 files changed, 560 insertions(+), 262 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/elasticsearch_index.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.scss create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/illustration.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/elasticsearch_index.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/elasticsearch_index.tsx new file mode 100644 index 0000000000000..8fec70d7abcdd --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/elasticsearch_index.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues, useActions } from 'kea'; + +import { + EuiFlyoutHeader, + EuiLink, + EuiTitle, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiButtonEmpty, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { CANCEL_BUTTON_LABEL } from '../../../../shared/constants'; + +import { FLYOUT_ARIA_LABEL_ID } from '../constants'; +import { Errors } from '../creation_response_components'; +import { DocumentCreationLogic } from '../index'; + +import './paste_json_text.scss'; + +export const ElasticsearchIndex: React.FC = () => ( + <> + + + + +); + +export const FlyoutHeader: React.FC = () => { + return ( + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.elasticsearchIndex.title', + { + defaultMessage: 'Connect an Elasticsearch index', + } + )} +

+ + + ); +}; + +export const FlyoutBody: React.FC = () => { + return ( + }> + +

+ + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.elasticsearchIndex.link', + { + defaultMessage: 'Learn more about using an existing index', + } + )} + + ), + }} + /> +

+
+ + {'{Form fields go here}'} +
+ ); +}; + +export const FlyoutFooter: React.FC = () => { + // TODO: replace these + const { textInput, isUploading } = useValues(DocumentCreationLogic); + // TODO: replace 'onSubmitJson' + const { onSubmitJson, closeDocumentCreation } = useActions(DocumentCreationLogic); + + return ( + + + + {CANCEL_BUTTON_LABEL} + + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.elasticsearchIndex.button', + { + defaultMessage: 'Connect to index', + } + )} + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/index.ts index 8fd199f9d6022..05ccb398c68f9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/index.ts @@ -7,5 +7,7 @@ export { ShowCreationModes } from './show_creation_modes'; export { ApiCodeExample } from './api_code_example'; -export { PasteJsonText } from './paste_json_text'; -export { UploadJsonFile } from './upload_json_file'; +export { JsonFlyout } from './json_flyout'; +export { ElasticsearchIndex } from './elasticsearch_index'; +export { PasteJsonTextTabContent, PasteJsonTextFooterContent } from './paste_json_text'; +export { UploadJsonFileTabContent, UploadJsonFileFooterContent } from './upload_json_file'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.scss new file mode 100644 index 0000000000000..310cf4dac95da --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.scss @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +.enterpriseSearchTabbedFlyoutHeader.euiFlyoutHeader { + padding-bottom: 0; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.test.tsx new file mode 100644 index 0000000000000..a31a46007a8b4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.test.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues, setMockActions } from '../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiFlyoutHeader, EuiFlyoutBody, EuiFlyoutFooter, EuiTabs, EuiTab } from '@elastic/eui'; + +import { + JsonFlyout, + PasteJsonTextTabContent, + UploadJsonFileTabContent, + PasteJsonTextFooterContent, + UploadJsonFileFooterContent, +} from './'; + +describe('JsonFlyout', () => { + const values = { + activeJsonTab: 'uploadTab', + }; + const actions = { + closeDocumentCreation: jest.fn(), + setActiveJsonTab: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(values); + setMockActions(actions); + }); + + it('renders a flyout components', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiFlyoutHeader)).toHaveLength(1); + expect(wrapper.find(EuiFlyoutBody)).toHaveLength(1); + expect(wrapper.find(EuiFlyoutFooter)).toHaveLength(1); + }); + + it('renders Upload json components and calls method with correct param', () => { + const wrapper = shallow(); + const tabs = wrapper.find(EuiTabs).find(EuiTab); + + expect(tabs).toHaveLength(2); + + tabs.at(1).simulate('click'); + + expect(actions.setActiveJsonTab).toHaveBeenCalledWith('pasteTab'); + expect(wrapper.find(UploadJsonFileTabContent)).toHaveLength(1); + expect(wrapper.find(UploadJsonFileFooterContent)).toHaveLength(1); + }); + + it('renders Paste json components and calls method with correct param', () => { + setMockValues({ ...values, activeJsonTab: 'pasteTab' }); + const wrapper = shallow(); + const tabs = wrapper.find(EuiTabs).find(EuiTab); + + expect(tabs).toHaveLength(2); + + tabs.at(0).simulate('click'); + + expect(actions.setActiveJsonTab).toHaveBeenCalledWith('uploadTab'); + expect(wrapper.find(PasteJsonTextTabContent)).toHaveLength(1); + expect(wrapper.find(PasteJsonTextFooterContent)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.tsx new file mode 100644 index 0000000000000..3f78c10ee2195 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/json_flyout.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues, useActions } from 'kea'; + +import { + EuiFlyoutHeader, + EuiTitle, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiSpacer, + EuiTabs, + EuiTab, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { FLYOUT_ARIA_LABEL_ID } from '../constants'; +import { Errors } from '../creation_response_components'; +import { DocumentCreationLogic, ActiveJsonTab } from '../index'; + +import { + PasteJsonTextTabContent, + UploadJsonFileTabContent, + PasteJsonTextFooterContent, + UploadJsonFileFooterContent, +} from './'; + +import './json_flyout.scss'; + +export const JsonFlyout: React.FC = () => { + const { activeJsonTab } = useValues(DocumentCreationLogic); + const { setActiveJsonTab } = useActions(DocumentCreationLogic); + + const tabs = [ + { + id: 'uploadTab', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.jsonFlyout.uploadTabName', + { + defaultMessage: 'Upload', + } + ), + content: , + }, + { + id: 'pasteTab', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.jsonFlyout.pasteTabName', + { + defaultMessage: 'Paste', + } + ), + content: , + }, + ]; + + return ( + <> + + +

+ {i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.jsonFlyout.title', { + defaultMessage: 'Paste or upload JSON', + })} +

+
+ + + {tabs.map((tab, index) => ( + setActiveJsonTab(tab.id as ActiveJsonTab)} + isSelected={tab.id === activeJsonTab} + > + {tab.name} + + ))} + +
+ }> + {tabs.find((tab) => tab.id === activeJsonTab)?.content} + + + {activeJsonTab === 'uploadTab' ? ( + + ) : ( + + )} + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx index 863e87a28f40e..492b9593e5c8e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.test.tsx @@ -15,9 +15,7 @@ import { EuiTextArea, EuiButtonEmpty, EuiButton } from '@elastic/eui'; import { rerender } from '../../../../test_helpers'; -import { Errors } from '../creation_response_components'; - -import { PasteJsonText, FlyoutHeader, FlyoutBody, FlyoutFooter } from './paste_json_text'; +import { PasteJsonTextTabContent, PasteJsonTextFooterContent } from './paste_json_text'; describe('PasteJsonText', () => { const values = { @@ -42,24 +40,10 @@ describe('PasteJsonText', () => { setMockActions(actions); }); - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.find(FlyoutHeader)).toHaveLength(1); - expect(wrapper.find(FlyoutBody)).toHaveLength(1); - expect(wrapper.find(FlyoutFooter)).toHaveLength(1); - }); - - describe('FlyoutHeader', () => { - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.find('h2').text()).toEqual('Create documents'); - }); - }); - - describe('FlyoutBody', () => { + describe('PasteJsonTextTabContent', () => { it('renders and updates the textarea value', () => { setMockValues({ ...values, textInput: 'lorem ipsum' }); - const wrapper = shallow(); + const wrapper = shallow(); const textarea = wrapper.find(EuiTextArea); expect(textarea.prop('value')).toEqual('lorem ipsum'); @@ -67,35 +51,25 @@ describe('PasteJsonText', () => { textarea.simulate('change', { target: { value: 'dolor sit amet' } }); expect(actions.setTextInput).toHaveBeenCalledWith('dolor sit amet'); }); - - it('shows an error banner and sets invalid form props if errors exist', () => { - const wrapper = shallow(); - expect(wrapper.find(EuiTextArea).prop('isInvalid')).toBe(false); - - setMockValues({ ...values, errors: ['some error'] }); - rerender(wrapper); - expect(wrapper.find(EuiTextArea).prop('isInvalid')).toBe(true); - expect(wrapper.prop('banner').type).toEqual(Errors); - }); }); - describe('FlyoutFooter', () => { + describe('PasteJsonTextFooterContent', () => { it('closes the modal', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.find(EuiButtonEmpty).simulate('click'); expect(actions.closeDocumentCreation).toHaveBeenCalled(); }); it('submits json', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.find(EuiButton).simulate('click'); expect(actions.onSubmitJson).toHaveBeenCalled(); }); it('disables/enables the Continue button based on whether text has been entered', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(false); setMockValues({ ...values, textInput: '' }); @@ -104,7 +78,7 @@ describe('PasteJsonText', () => { }); it('sets isLoading based on isUploading', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiButton).prop('isLoading')).toBe(false); setMockValues({ ...values, isUploading: true }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx index 33f1187a88552..4eae3b4f520de 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/paste_json_text.tsx @@ -10,12 +10,8 @@ import React from 'react'; import { useValues, useActions } from 'kea'; import { - EuiFlyoutHeader, - EuiTitle, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlexGroup, EuiFlexItem, + EuiFlexGroup, EuiButton, EuiButtonEmpty, EuiTextArea, @@ -27,35 +23,11 @@ import { i18n } from '@kbn/i18n'; import { CANCEL_BUTTON_LABEL, CONTINUE_BUTTON_LABEL } from '../../../../shared/constants'; import { AppLogic } from '../../../app_logic'; -import { FLYOUT_ARIA_LABEL_ID } from '../constants'; -import { Errors } from '../creation_response_components'; import { DocumentCreationLogic } from '../index'; import './paste_json_text.scss'; -export const PasteJsonText: React.FC = () => ( - <> - - - - -); - -export const FlyoutHeader: React.FC = () => { - return ( - - -

- {i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.title', { - defaultMessage: 'Create documents', - })} -

-
-
- ); -}; - -export const FlyoutBody: React.FC = () => { +export const PasteJsonTextTabContent: React.FC = () => { const { configuredLimits: { engine: { maxDocumentByteSize }, @@ -66,7 +38,7 @@ export const FlyoutBody: React.FC = () => { const { setTextInput } = useActions(DocumentCreationLogic); return ( - }> + <>

{i18n.translate( @@ -92,26 +64,24 @@ export const FlyoutBody: React.FC = () => { fullWidth rows={12} /> - + ); }; -export const FlyoutFooter: React.FC = () => { +export const PasteJsonTextFooterContent: React.FC = () => { const { textInput, isUploading } = useValues(DocumentCreationLogic); const { onSubmitJson, closeDocumentCreation } = useActions(DocumentCreationLogic); return ( - - - - {CANCEL_BUTTON_LABEL} - - - - {CONTINUE_BUTTON_LABEL} - - - - + + + {CANCEL_BUTTON_LABEL} + + + + {CONTINUE_BUTTON_LABEL} + + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx index e1ba5ca354aea..ff34f0ff7a0b7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/show_creation_modes.tsx @@ -38,7 +38,7 @@ export const ShowCreationModes: React.FC = () => { - + {CANCEL_BUTTON_LABEL} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx index 9b8820434fad8..38689279c8fce 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.test.tsx @@ -15,9 +15,7 @@ import { EuiFilePicker, EuiButtonEmpty, EuiButton } from '@elastic/eui'; import { rerender } from '../../../../test_helpers'; -import { Errors } from '../creation_response_components'; - -import { UploadJsonFile, FlyoutHeader, FlyoutBody, FlyoutFooter } from './upload_json_file'; +import { UploadJsonFileTabContent, UploadJsonFileFooterContent } from './upload_json_file'; describe('UploadJsonFile', () => { const mockFile = new File(['mock'], 'mock.json', { type: 'application/json' }); @@ -43,23 +41,9 @@ describe('UploadJsonFile', () => { setMockActions(actions); }); - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.find(FlyoutHeader)).toHaveLength(1); - expect(wrapper.find(FlyoutBody)).toHaveLength(1); - expect(wrapper.find(FlyoutFooter)).toHaveLength(1); - }); - - describe('FlyoutHeader', () => { - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.find('h2').text()).toEqual('Drag and drop .json'); - }); - }); - - describe('FlyoutBody', () => { + describe('UploadJsonFileTabContent', () => { it('updates fileInput when files are added & removed', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.find(EuiFilePicker).simulate('change', [mockFile]); expect(actions.setFileInput).toHaveBeenCalledWith(mockFile); @@ -69,42 +53,32 @@ describe('UploadJsonFile', () => { }); it('sets isLoading based on isUploading', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiFilePicker).prop('isLoading')).toBe(false); setMockValues({ ...values, isUploading: true }); rerender(wrapper); expect(wrapper.find(EuiFilePicker).prop('isLoading')).toBe(true); }); - - it('shows an error banner and sets invalid form props if errors exist', () => { - const wrapper = shallow(); - expect(wrapper.find(EuiFilePicker).prop('isInvalid')).toBe(false); - - setMockValues({ ...values, errors: ['some error'] }); - rerender(wrapper); - expect(wrapper.find(EuiFilePicker).prop('isInvalid')).toBe(true); - expect(wrapper.prop('banner').type).toEqual(Errors); - }); }); - describe('FlyoutFooter', () => { + describe('UploadJsonFileFooterContent', () => { it('closes the flyout', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.find(EuiButtonEmpty).simulate('click'); expect(actions.closeDocumentCreation).toHaveBeenCalled(); }); it('submits the json file', () => { - const wrapper = shallow(); + const wrapper = shallow(); wrapper.find(EuiButton).simulate('click'); expect(actions.onSubmitFile).toHaveBeenCalled(); }); it('disables/enables the Continue button based on whether files have been uploaded', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiButton).prop('isDisabled')).toBe(true); setMockValues({ ...values, fineInput: mockFile }); @@ -113,7 +87,7 @@ describe('UploadJsonFile', () => { }); it('sets isLoading based on isUploading', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiButton).prop('isLoading')).toBe(false); setMockValues({ ...values, isUploading: true }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx index ceddd406d19c5..ec172f0434dc3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/upload_json_file.tsx @@ -10,10 +10,6 @@ import React from 'react'; import { useValues, useActions } from 'kea'; import { - EuiFlyoutHeader, - EuiTitle, - EuiFlyoutBody, - EuiFlyoutFooter, EuiFlexGroup, EuiFlexItem, EuiButton, @@ -27,34 +23,9 @@ import { i18n } from '@kbn/i18n'; import { CANCEL_BUTTON_LABEL, CONTINUE_BUTTON_LABEL } from '../../../../shared/constants'; import { AppLogic } from '../../../app_logic'; -import { FLYOUT_ARIA_LABEL_ID } from '../constants'; -import { Errors } from '../creation_response_components'; import { DocumentCreationLogic } from '../index'; -export const UploadJsonFile: React.FC = () => ( - <> - - - - -); - -export const FlyoutHeader: React.FC = () => { - return ( - - -

- {i18n.translate( - 'xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.title', - { defaultMessage: 'Drag and drop .json' } - )} -

- - - ); -}; - -export const FlyoutBody: React.FC = () => { +export const UploadJsonFileTabContent: React.FC = () => { const { configuredLimits: { engine: { maxDocumentByteSize }, @@ -65,7 +36,7 @@ export const FlyoutBody: React.FC = () => { const { setFileInput } = useActions(DocumentCreationLogic); return ( - }> + <>

{i18n.translate( @@ -86,26 +57,24 @@ export const FlyoutBody: React.FC = () => { isLoading={isUploading} isInvalid={errors.length > 0} /> - + ); }; -export const FlyoutFooter: React.FC = () => { +export const UploadJsonFileFooterContent: React.FC = () => { const { fileInput, isUploading } = useValues(DocumentCreationLogic); const { onSubmitFile, closeDocumentCreation } = useActions(DocumentCreationLogic); return ( - - - - {CANCEL_BUTTON_LABEL} - - - - {CONTINUE_BUTTON_LABEL} - - - - + + + {CANCEL_BUTTON_LABEL} + + + + {CONTINUE_BUTTON_LABEL} + + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx index cef0a8d05ec6b..8fa0f86896c30 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx @@ -15,7 +15,7 @@ import { useLocation } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { EuiCard } from '@elastic/eui'; +import { EuiCard, EuiText } from '@elastic/eui'; import { EuiCardTo } from '../../../shared/react_router_helpers'; @@ -45,17 +45,23 @@ describe('DocumentCreationButtons', () => { expect(wrapper.find(EuiCardTo).prop('isDisabled')).toEqual(true); }); + it('renders with flyoutHeader', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiText)).toHaveLength(1); + }); + it('opens the DocumentCreationFlyout on click', () => { const wrapper = shallow(); wrapper.find(EuiCard).at(0).simulate('click'); - expect(actions.openDocumentCreation).toHaveBeenCalledWith('text'); + expect(actions.openDocumentCreation).toHaveBeenCalledWith('json'); wrapper.find(EuiCard).at(1).simulate('click'); - expect(actions.openDocumentCreation).toHaveBeenCalledWith('file'); + expect(actions.openDocumentCreation).toHaveBeenCalledWith('api'); wrapper.find(EuiCard).at(2).simulate('click'); - expect(actions.openDocumentCreation).toHaveBeenCalledWith('api'); + expect(actions.openDocumentCreation).toHaveBeenCalledWith('elasticsearchIndex'); }); it('renders the crawler button with a link to the crawler page', () => { @@ -64,12 +70,12 @@ describe('DocumentCreationButtons', () => { expect(wrapper.find(EuiCardTo).prop('to')).toEqual('/engines/some-engine/crawler'); }); - it('calls openDocumentCreation("file") if ?method=json', () => { + it('calls openDocumentCreation("json") if ?method=json', () => { const search = '?method=json'; (useLocation as jest.Mock).mockImplementationOnce(() => ({ search })); shallow(); - expect(actions.openDocumentCreation).toHaveBeenCalledWith('file'); + expect(actions.openDocumentCreation).toHaveBeenCalledWith('json'); }); it('calls openDocumentCreation("api") if ?method=api', () => { @@ -79,4 +85,12 @@ describe('DocumentCreationButtons', () => { shallow(); expect(actions.openDocumentCreation).toHaveBeenCalledWith('api'); }); + + it('calls openDocumentCreation("elasticsearchIndex") if ?method=elasticsearchIndex', () => { + const search = '?method=elasticsearchIndex'; + (useLocation as jest.Mock).mockImplementationOnce(() => ({ search })); + + shallow(); + expect(actions.openDocumentCreation).toHaveBeenCalledWith('elasticsearchIndex'); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx index a8179f297644f..edc91804961e1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx @@ -13,30 +13,37 @@ import { Location } from 'history'; import { useActions } from 'kea'; import { + EuiEmptyPrompt, EuiText, - EuiCode, + EuiTitle, + EuiImage, EuiLink, EuiSpacer, - EuiFlexGrid, + EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import { parseQueryParams } from '../../../shared/query_params'; import { EuiCardTo } from '../../../shared/react_router_helpers'; import { INDEXING_DOCS_URL, ENGINE_CRAWLER_PATH } from '../../routes'; import { generateEnginePath } from '../engine'; +import illustration from './illustration.svg'; + import { DocumentCreationLogic } from './'; interface Props { + isFlyout?: boolean; disabled?: boolean; } -export const DocumentCreationButtons: React.FC = ({ disabled = false }) => { +export const DocumentCreationButtons: React.FC = ({ + isFlyout = false, + disabled = false, +}) => { const { openDocumentCreation } = useActions(DocumentCreationLogic); const { search } = useLocation() as Location; @@ -45,90 +52,156 @@ export const DocumentCreationButtons: React.FC = ({ disabled = false }) = useEffect(() => { switch (method) { case 'json': - openDocumentCreation('file'); + openDocumentCreation('json'); break; case 'api': openDocumentCreation('api'); break; + case 'elasticsearchIndex': + openDocumentCreation('elasticsearchIndex'); + break; } }, []); const crawlerLink = generateEnginePath(ENGINE_CRAWLER_PATH); - return ( - <> - -

- .json, - postCode: POST, - documentsApiLink: ( - - documents API - - ), - }} + const helperText = ( +

+ {i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.helperText', { + defaultMessage: + 'There are four ways to send documents to your engine for indexing. You can paste or upload a JSON file, POST to the documents API endpoint, connect to an existing Elasticsearch index, or use the Elastic Web Crawler to automatically index documents from a URL.', + })} +

+ ); + + const emptyState = ( + + -

-
+ } + title={ +

+ {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.emptyStateTitle', + { defaultMessage: 'Add documents' } + )} +

+ } + layout="horizontal" + hasBorder + color="plain" + body={helperText} + footer={ + <> + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.emptyStateFooterText', + { defaultMessage: 'Want to learn more about indexing documents?' } + )} + + {' '} + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.emptyStateFooterLink', + { defaultMessage: 'Read documentation' } + )} + + + } + /> + + ); + + const flyoutHeader = ( + <> + {helperText} - - + + ); + + return ( + <> + {isFlyout && flyoutHeader} + + } + description={i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.crawlDescription', + { defaultMessage: 'Automatically index documents from a URL' } + )} + icon={} to={crawlerLink} isDisabled={disabled} /> - - + } + description={i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.jsonDescription', + { defaultMessage: 'Add documents by pasting or uploading raw JSON' } + )} + icon={} data-test-subj="IndexingPasteJSONButton" - onClick={() => openDocumentCreation('text')} + onClick={() => openDocumentCreation('json')} isDisabled={disabled} /> - - + } - onClick={() => openDocumentCreation('file')} + icon={} + onClick={() => openDocumentCreation('api')} isDisabled={disabled} /> - - + } - onClick={() => openDocumentCreation('api')} + hasBorder + layout="horizontal" + title={i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.elasticsearchTitle', + { defaultMessage: 'Use an Elasticsearch Index' } + )} + description={i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentCreation.buttons.elasticsearchDescription', + { defaultMessage: 'Search your existing indices with App Search' } + )} + icon={} + onClick={() => openDocumentCreation('elasticsearchIndex')} isDisabled={disabled} /> - + {!isFlyout && emptyState} + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx index 3dc5d445930ba..3bc7eac1a9e69 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.test.tsx @@ -16,8 +16,8 @@ import { EuiFlyout } from '@elastic/eui'; import { ShowCreationModes, ApiCodeExample, - PasteJsonText, - UploadJsonFile, + JsonFlyout, + ElasticsearchIndex, } from './creation_mode_components'; import { Summary } from './creation_response_components'; import { DocumentCreationFlyout, FlyoutContent } from './document_creation_flyout'; @@ -26,11 +26,12 @@ import { DocumentCreationStep } from './types'; describe('DocumentCreationFlyout', () => { const values = { isDocumentCreationOpen: true, - creationMode: 'text', + creationMode: 'api', creationStep: DocumentCreationStep.AddDocuments, }; const actions = { closeDocumentCreation: jest.fn(), + setActiveJsonTab: jest.fn(), }; beforeEach(() => { @@ -70,18 +71,18 @@ describe('DocumentCreationFlyout', () => { expect(wrapper.find(ApiCodeExample)).toHaveLength(1); }); - it('renders PasteJsonText', () => { - setMockValues({ ...values, creationMode: 'text' }); + it('renders JsonFlyout', () => { + setMockValues({ ...values, creationMode: 'json' }); const wrapper = shallow(); - expect(wrapper.find(PasteJsonText)).toHaveLength(1); + expect(wrapper.find(JsonFlyout)).toHaveLength(1); }); - it('renders UploadJsonFile', () => { - setMockValues({ ...values, creationMode: 'file' }); + it('renders ElasticsearchIndex', () => { + setMockValues({ ...values, creationMode: 'elasticsearchIndex' }); const wrapper = shallow(); - expect(wrapper.find(UploadJsonFile)).toHaveLength(1); + expect(wrapper.find(ElasticsearchIndex)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx index 159f3403d3740..2e8111eb00f1e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_flyout.tsx @@ -15,8 +15,8 @@ import { FLYOUT_ARIA_LABEL_ID } from './constants'; import { ShowCreationModes, ApiCodeExample, - PasteJsonText, - UploadJsonFile, + JsonFlyout, + ElasticsearchIndex, } from './creation_mode_components'; import { Summary } from './creation_response_components'; import { DocumentCreationStep } from './types'; @@ -46,10 +46,10 @@ export const FlyoutContent: React.FC = () => { switch (creationMode) { case 'api': return ; - case 'text': - return ; - case 'file': - return ; + case 'json': + return ; + case 'elasticsearchIndex': + return ; } case DocumentCreationStep.ShowSummary: return ; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts index edd5c5566a45d..e0f49c788a9e1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.test.ts @@ -31,7 +31,8 @@ describe('DocumentCreationLogic', () => { const DEFAULT_VALUES = { isDocumentCreationOpen: false, - creationMode: 'text', + creationMode: 'api', + activeJsonTab: 'uploadTab', creationStep: DocumentCreationStep.AddDocuments, textInput: dedent(DOCUMENTS_API_JSON_EXAMPLE), fileInput: null, @@ -124,6 +125,24 @@ describe('DocumentCreationLogic', () => { }); }); + describe('setActiveJsonTab', () => { + beforeAll(() => { + mount(); + DocumentCreationLogic.actions.setActiveJsonTab('pasteTab'); + }); + + const EXPECTED_VALUES = { + ...DEFAULT_VALUES, + activeJsonTab: 'pasteTab', + }; + + describe('isDocumentCreationOpen', () => { + it('should be set to "pasteTab"', () => { + expect(DocumentCreationLogic.values).toEqual(EXPECTED_VALUES); + }); + }); + }); + describe('closeDocumentCreation', () => { describe('isDocumentCreationOpen', () => { it('should be set to false', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts index 5ea2f0fe7cf73..b67b37b5dbb3b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_logic.ts @@ -20,10 +20,13 @@ import { import { DocumentCreationMode, DocumentCreationStep, DocumentCreationSummary } from './types'; import { readUploadedFileAsText } from './utils'; +export type ActiveJsonTab = 'uploadTab' | 'pasteTab'; + interface DocumentCreationValues { isDocumentCreationOpen: boolean; creationMode: DocumentCreationMode; creationStep: DocumentCreationStep; + activeJsonTab: ActiveJsonTab; textInput: string; fileInput: File | null; isUploading: boolean; @@ -37,6 +40,7 @@ interface DocumentCreationActions { openDocumentCreation(creationMode: DocumentCreationMode): { creationMode: DocumentCreationMode }; closeDocumentCreation(): void; setCreationStep(creationStep: DocumentCreationStep): { creationStep: DocumentCreationStep }; + setActiveJsonTab(activeJsonTab: ActiveJsonTab): { activeJsonTab: ActiveJsonTab }; setTextInput(textInput: string): { textInput: string }; setFileInput(fileInput: File | null): { fileInput: File | null }; setWarnings(warnings: string[]): { warnings: string[] }; @@ -56,6 +60,7 @@ export const DocumentCreationLogic = kea< openDocumentCreation: (creationMode) => ({ creationMode }), closeDocumentCreation: () => null, setCreationStep: (creationStep) => ({ creationStep }), + setActiveJsonTab: (activeJsonTab) => ({ activeJsonTab }), setTextInput: (textInput) => ({ textInput }), setFileInput: (fileInput) => ({ fileInput }), setWarnings: (warnings) => ({ warnings }), @@ -75,11 +80,17 @@ export const DocumentCreationLogic = kea< }, ], creationMode: [ - 'text', + 'api', { openDocumentCreation: (_, { creationMode }) => creationMode, }, ], + activeJsonTab: [ + 'uploadTab', + { + setActiveJsonTab: (_, { activeJsonTab }) => activeJsonTab, + }, + ], creationStep: [ DocumentCreationStep.AddDocuments, { @@ -93,6 +104,7 @@ export const DocumentCreationLogic = kea< { setTextInput: (_, { textInput }) => textInput, closeDocumentCreation: () => dedent(DOCUMENTS_API_JSON_EXAMPLE), + setActiveJsonTab: () => dedent(DOCUMENTS_API_JSON_EXAMPLE), }, ], fileInput: [ @@ -100,6 +112,7 @@ export const DocumentCreationLogic = kea< { setFileInput: (_, { fileInput }) => fileInput, closeDocumentCreation: () => null, + setActiveJsonTab: () => null, }, ], isUploading: [ @@ -109,6 +122,7 @@ export const DocumentCreationLogic = kea< onSubmitJson: () => true, setErrors: () => false, setSummary: () => false, + setActiveJsonTab: () => false, }, ], warnings: [ @@ -117,6 +131,7 @@ export const DocumentCreationLogic = kea< onSubmitJson: () => [], setWarnings: (_, { warnings }) => warnings, closeDocumentCreation: () => [], + setActiveJsonTab: () => [], }, ], errors: [ @@ -125,6 +140,7 @@ export const DocumentCreationLogic = kea< onSubmitJson: () => [], setErrors: (_, { errors }) => (Array.isArray(errors) ? errors : [errors]), closeDocumentCreation: () => [], + setActiveJsonTab: () => [], }, ], summary: [ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/illustration.svg b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/illustration.svg new file mode 100644 index 0000000000000..6af40daf69d60 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/illustration.svg @@ -0,0 +1 @@ + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/index.ts index 9caf0749cfb29..4bf51e50aad30 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/index.ts @@ -8,3 +8,4 @@ export { DocumentCreationButtons } from './document_creation_buttons'; export { DocumentCreationFlyout } from './document_creation_flyout'; export { DocumentCreationLogic } from './document_creation_logic'; +export type { ActiveJsonTab } from './document_creation_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/types.ts index 63968b7e99831..33360f4673088 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -export type DocumentCreationMode = 'text' | 'file' | 'api'; +export type DocumentCreationMode = 'api' | 'json' | 'elasticsearchIndex'; export enum DocumentCreationStep { ShowCreationModes, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 99e4c6a4ca1f3..b5b597908c041 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -8723,11 +8723,9 @@ "xpack.enterpriseSearch.appSearch.documentCreation.api.description": "L'{documentsApiLink} peut être utilisée pour ajouter de nouveaux documents à votre moteur, mettre à jour des documents, récupérer des documents par ID et supprimer des documents. Il existe un grand nombre de {clientLibrariesLink} pour vous aider à démarrer.", "xpack.enterpriseSearch.appSearch.documentCreation.api.example": "Pour voir l'API en action, vous pouvez tester l'exemple de requête ci-dessous à l'aide d'une ligne de commande ou d'une bibliothèque client.", "xpack.enterpriseSearch.appSearch.documentCreation.api.title": "Indexation par API", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.api": "Index de l'API", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.crawl": "Utiliser le robot d'indexation", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.file": "Charger un fichier JSON", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.text": "Coller le JSON", - "xpack.enterpriseSearch.appSearch.documentCreation.description": "Vous disposez de quatre choix pour envoyer les documents à votre moteur à des fins d'indexation. Vous pouvez coller le JSON brut, charger un fichier {jsonCode}, {postCode} sur le point de terminaison de l'{documentsApiLink} ou tester le nouveau robot d'indexation Elastic (bêta) pour indexer automatiquement les documents à partir d'une URL. Cliquez sur votre choix ci-dessous.", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.apiTitle": "Index de l'API", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.crawlTitle": "Utiliser le robot d'indexation", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.jsonTitle": "Coller le JSON", "xpack.enterpriseSearch.appSearch.documentCreation.errorsTitle": "Un problème est survenu. Veuillez corriger les erreurs et réessayer.", "xpack.enterpriseSearch.appSearch.documentCreation.largeFile": "Vous êtes en train de charger un fichier extrêmement volumineux. Cette opération risque de bloquer votre navigateur ou de prendre beaucoup de temps à traiter. Si possible, essayez de diviser vos données en plusieurs fichiers plus petits.", "xpack.enterpriseSearch.appSearch.documentCreation.noFileFound": "Aucun fichier trouvé.", @@ -8735,7 +8733,6 @@ "xpack.enterpriseSearch.appSearch.documentCreation.noValidFile": "Problème lors de l'analyse du fichier.", "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.description": "Collez un tableau de documents JSON. Vérifiez que le JSON est valide et que la taille de chaque objet de document est inférieure à {maxDocumentByteSize} octets.", "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.label": "Coller le JSON ici", - "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.title": "Créer des documents", "xpack.enterpriseSearch.appSearch.documentCreation.showCreationModes.title": "Ajouter de nouveaux documents", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.documentNotIndexed": "Ce document n'a pas été indexé !", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.fixErrors": "Corriger les erreurs", @@ -8747,7 +8744,6 @@ "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.otherDocuments": "et {documents, number} {documents, plural, one {autre document} other {autres documents}}.", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.title": "Résumé de l'indexation", "xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.label": "Si vous avez un fichier .json, chargez-le ou effectuez un glisser-déposer du fichier. Vérifiez que le JSON est valide et que la taille de chaque objet de document est inférieure à {maxDocumentByteSize} octets.", - "xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.title": "Glisser-déposer .json", "xpack.enterpriseSearch.appSearch.documentCreation.warningsTitle": "Avertissement !", "xpack.enterpriseSearch.appSearch.documentDetail.confirmDelete": "Voulez-vous vraiment supprimer ce document ?", "xpack.enterpriseSearch.appSearch.documentDetail.deleteSuccess": "Votre document a été supprimé", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a80c43bd832d8..412b3706f278c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10261,11 +10261,9 @@ "xpack.enterpriseSearch.appSearch.documentCreation.api.description": "{documentsApiLink}を使用すると、新しいドキュメントをエンジンに追加できるほか、ドキュメントの更新、IDによるドキュメントの取得、ドキュメントの削除が可能です。基本操作を説明するさまざまな{clientLibrariesLink}があります。", "xpack.enterpriseSearch.appSearch.documentCreation.api.example": "実行中のAPIを表示するには、コマンドラインまたはクライアントライブラリを使用して、次の要求の例で実験することができます。", "xpack.enterpriseSearch.appSearch.documentCreation.api.title": "APIでインデックス", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.api": "API からインデックス", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.crawl": "Crawler を使用", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.file": "JSON ファイルのアップロード", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.text": "JSON の貼り付け", - "xpack.enterpriseSearch.appSearch.documentCreation.description": "ドキュメントをインデックスのためにエンジンに送信するには、4 つの方法があります。未加工の JSON を貼り付け、{jsonCode} ファイル {postCode} を {documentsApiLink} エンドポイントにアップロードするか、新しい Elastic Crawlerを使用して、自動的に URL からドキュメントにインデックスすることができます。以下の選択肢をクリックします。", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.apiTitle": "API からインデックス", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.crawlTitle": "Crawler を使用", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.jsonTitle": "JSON の貼り付け", "xpack.enterpriseSearch.appSearch.documentCreation.errorsTitle": "何か問題が発生しましたエラーを解決して、再試行してください。", "xpack.enterpriseSearch.appSearch.documentCreation.largeFile": "非常に大きいファイルをアップロードしています。ブラウザーがロックされたり、処理に非常に時間がかかったりする可能性があります。可能な場合は、データを複数の小さいファイルに分割してください。", "xpack.enterpriseSearch.appSearch.documentCreation.noFileFound": "ファイルが見つかりません。", @@ -10273,7 +10271,6 @@ "xpack.enterpriseSearch.appSearch.documentCreation.noValidFile": "ファイル解析の問題。", "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.description": "JSONドキュメントの配列を貼り付けます。JSONが有効であり、各ドキュメントオブジェクトが{maxDocumentByteSize}バイト未満であることを確認してください。", "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.label": "ここにJSONを貼り付け", - "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.title": "ドキュメントの作成", "xpack.enterpriseSearch.appSearch.documentCreation.showCreationModes.title": "新しいドキュメントの追加", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.documentNotIndexed": "このドキュメントにはインデックスが作成されていません。", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.fixErrors": "エラーを修正してください", @@ -10285,7 +10282,6 @@ "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.otherDocuments": "と{documents, number} other {documents, plural, other {個のドキュメント}}。", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.title": "インデックス概要", "xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.label": ".jsonファイルがある場合は、ドラッグアンドドロップするか、アップロードします。JSONが有効であり、各ドキュメントオブジェクトが{maxDocumentByteSize}バイト未満であることを確認してください。", - "xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.title": ".jsonをドラッグアンドドロップ", "xpack.enterpriseSearch.appSearch.documentCreation.warningsTitle": "警告!", "xpack.enterpriseSearch.appSearch.documentDetail.confirmDelete": "このドキュメントを削除しますか?", "xpack.enterpriseSearch.appSearch.documentDetail.deleteSuccess": "ドキュメントは削除されました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 15a194346a9e1..0b97869ad0a4a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10282,11 +10282,9 @@ "xpack.enterpriseSearch.appSearch.documentCreation.api.description": "{documentsApiLink} 可用于将新文档添加到您的引擎、更新文档、按 ID 检索文档以及删除文档。有各种{clientLibrariesLink}可帮助您入门。", "xpack.enterpriseSearch.appSearch.documentCreation.api.example": "要了解如何使用 API,可以在下面通过命令行或客户端库试用示例请求。", "xpack.enterpriseSearch.appSearch.documentCreation.api.title": "按 API 索引", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.api": "从 API 索引", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.crawl": "使用网络爬虫", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.file": "上传 JSON 文件", - "xpack.enterpriseSearch.appSearch.documentCreation.buttons.text": "粘贴 JSON", - "xpack.enterpriseSearch.appSearch.documentCreation.description": "有四种方法将文档发送到引擎进行索引。您可以粘贴原始 JSON、上传 {jsonCode} 文件、{postCode} 到 {documentsApiLink} 终端,或者使用新的 Elastic 网络爬虫来自动索引 URL 的文档。单击下面的选项。", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.apiTitle": "从 API 索引", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.crawlTitle": "使用网络爬虫", + "xpack.enterpriseSearch.appSearch.documentCreation.buttons.jsonTitle": "粘贴 JSON", "xpack.enterpriseSearch.appSearch.documentCreation.errorsTitle": "出问题了。请解决这些错误,然后重试。", "xpack.enterpriseSearch.appSearch.documentCreation.largeFile": "您正在上传极大的文件。这可能会锁定您的浏览器,或需要很长时间才能处理完。如果可能,请尝试将您的数据拆分成多个较小的文件。", "xpack.enterpriseSearch.appSearch.documentCreation.noFileFound": "未找到文件。", @@ -10294,7 +10292,6 @@ "xpack.enterpriseSearch.appSearch.documentCreation.noValidFile": "解析文件时出现问题。", "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.description": "粘贴一系列的 JSON 文档。确保 JSON 有效且每个文档对象小于 {maxDocumentByteSize} 字节。", "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.label": "在此处粘贴 JSON", - "xpack.enterpriseSearch.appSearch.documentCreation.pasteJsonText.title": "创建文档", "xpack.enterpriseSearch.appSearch.documentCreation.showCreationModes.title": "添加新文档", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.documentNotIndexed": "未索引此文档!", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.fixErrors": "修复错误", @@ -10306,7 +10303,6 @@ "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.otherDocuments": "及 {documents, number} 个其他{documents, plural, other {文档}}。", "xpack.enterpriseSearch.appSearch.documentCreation.showSummary.title": "索引摘要", "xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.label": "如果有 .json 文档,请拖放或上传。确保 JSON 有效且每个文档对象小于 {maxDocumentByteSize} 字节。", - "xpack.enterpriseSearch.appSearch.documentCreation.uploadJsonFile.title": "拖放 .json", "xpack.enterpriseSearch.appSearch.documentCreation.warningsTitle": "警告!", "xpack.enterpriseSearch.appSearch.documentDetail.confirmDelete": "是否确定要删除此文档?", "xpack.enterpriseSearch.appSearch.documentDetail.deleteSuccess": "您的文档已删除", From 68ed18831b29d4b3878b460155713225c012c1f3 Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Tue, 8 Mar 2022 16:27:51 -0500 Subject: [PATCH 082/140] [Security Solution] Adds CCS privileges warning enable switch in advanced settings (#124459) --- .../server/collectors/management/schema.ts | 4 ++++ .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 ++++++ .../plugins/security_solution/common/constants.ts | 3 +++ .../create_security_rule_type_wrapper.ts | 8 +++++++- .../server/lib/detection_engine/signals/utils.ts | 13 +++++++++++-- .../security_solution/server/ui_settings.ts | 14 ++++++++++++++ 7 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index bde8a10e4dd2e..0c742f351e99d 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -124,6 +124,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, + 'securitySolution:enableCcsWarning': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'search:includeFrozen': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 47656c568bf4c..7b571a1a44eb5 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -59,6 +59,7 @@ export interface UsageStats { 'securitySolution:defaultAnomalyScore': number; 'securitySolution:refreshIntervalDefaults': string; 'securitySolution:enableNewsFeed': boolean; + 'securitySolution:enableCcsWarning': boolean; 'search:includeFrozen': boolean; 'courier:maxConcurrentShardRequests': number; 'courier:setRequestPreference': string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index c21536ccf472a..ad0d4f88a96c8 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7337,6 +7337,12 @@ "description": "Non-default value of setting." } }, + "securitySolution:enableCcsWarning": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "search:includeFrozen": { "type": "boolean", "_meta": { diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index bc44b34021325..95132dc7894ff 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -170,6 +170,9 @@ export const DEFAULT_INDEX_PATTERN = [ /** This Kibana Advanced Setting enables the `Security news` feed widget */ export const ENABLE_NEWS_FEED_SETTING = 'securitySolution:enableNewsFeed' as const; +/** This Kibana Advanced Setting enables the warnings for CCS read permissions */ +export const ENABLE_CCS_READ_WARNING_SETTING = 'securitySolution:enableCcsWarning' as const; + /** This Kibana Advanced Setting sets the auto refresh interval for the detections all rules table */ export const DEFAULT_RULES_TABLE_REFRESH_SETTING = 'securitySolution:rulesTableRefresh' as const; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 1096573f81cd3..25b5471a3c2eb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -72,7 +72,12 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = } = options; let runState = state; const { from, maxSignals, meta, ruleId, timestampOverride, to } = params; - const { alertWithPersistence, savedObjectsClient, scopedClusterClient } = services; + const { + alertWithPersistence, + savedObjectsClient, + scopedClusterClient, + uiSettingsClient, + } = services; const searchAfterSize = Math.min(maxSignals, DEFAULT_SEARCH_AFTER_PAGE_SIZE); const esClient = scopedClusterClient.asCurrentUser; @@ -155,6 +160,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = logger, buildRuleMessage, ruleExecutionLogger, + uiSettingsClient, }); if (!wroteWarningStatus) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index b7a06d618162e..f49eee858d135 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -27,6 +27,7 @@ import { } from '../../../../common/detection_engine/schemas/common'; import type { ElasticsearchClient, + IUiSettingsClient, Logger, SavedObjectsClientContract, } from '../../../../../../../src/core/server'; @@ -65,6 +66,7 @@ import type { RACAlert, WrappedRACAlert } from '../rule_types/types'; import type { SearchTypes } from '../../../../common/detection_engine/types'; import type { IRuleExecutionLogForExecutors } from '../rule_execution_log'; import { withSecuritySpan } from '../../../utils/with_security_span'; +import { ENABLE_CCS_READ_WARNING_SETTING } from '../../../../common/constants'; interface SortExceptionsReturn { exceptionsWithValueLists: ExceptionListItemSchema[]; @@ -93,12 +95,19 @@ export const hasReadIndexPrivileges = async (args: { logger: Logger; buildRuleMessage: BuildRuleMessage; ruleExecutionLogger: IRuleExecutionLogForExecutors; + uiSettingsClient: IUiSettingsClient; }): Promise => { - const { privileges, logger, buildRuleMessage, ruleExecutionLogger } = args; + const { privileges, logger, buildRuleMessage, ruleExecutionLogger, uiSettingsClient } = args; + + const isCcsPermissionWarningEnabled = await uiSettingsClient.get(ENABLE_CCS_READ_WARNING_SETTING); const indexNames = Object.keys(privileges.index); + const filteredIndexNames = isCcsPermissionWarningEnabled + ? indexNames + : indexNames.filter((indexName) => !indexName.includes(':')); // Cross cluster indices uniquely contain `:` in their name + const [, indexesWithNoReadPrivileges] = partition( - indexNames, + filteredIndexNames, (indexName) => privileges.index[indexName].read ); diff --git a/x-pack/plugins/security_solution/server/ui_settings.ts b/x-pack/plugins/security_solution/server/ui_settings.ts index 6a90477540eb8..e3457a25aa7c4 100644 --- a/x-pack/plugins/security_solution/server/ui_settings.ts +++ b/x-pack/plugins/security_solution/server/ui_settings.ts @@ -32,6 +32,7 @@ import { IP_REPUTATION_LINKS_SETTING_DEFAULT, NEWS_FEED_URL_SETTING, NEWS_FEED_URL_SETTING_DEFAULT, + ENABLE_CCS_READ_WARNING_SETTING, } from '../common/constants'; import { transformConfigSchema } from '../common/transforms/types'; import { ExperimentalFeatures } from '../common/experimental_features'; @@ -218,6 +219,19 @@ export const initUiSettings = ( }) ), }, + [ENABLE_CCS_READ_WARNING_SETTING]: { + name: i18n.translate('xpack.securitySolution.uiSettings.enableCcsReadWarningLabel', { + defaultMessage: 'CCS Rule Privileges Warning', + }), + value: true, + description: i18n.translate('xpack.securitySolution.uiSettings.enableCcsWarningDescription', { + defaultMessage: '

Enables privilege check warnings in rules for CCS indices

', + }), + type: 'boolean', + category: [APP_ID], + requiresPageReload: false, + schema: schema.boolean(), + }, // TODO: Remove this check once the experimental flag is removed ...(experimentalFeatures.metricsEntitiesEnabled ? { From bb1834d297eeea6120fffc24a95911d78567321a Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Tue, 8 Mar 2022 15:50:06 -0600 Subject: [PATCH 083/140] [DOCS] Adds note for data source performance impact (#127184) --- docs/user/dashboard/aggregation-based.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/user/dashboard/aggregation-based.asciidoc b/docs/user/dashboard/aggregation-based.asciidoc index c4f26e701bccf..bf13661b9aadd 100644 --- a/docs/user/dashboard/aggregation-based.asciidoc +++ b/docs/user/dashboard/aggregation-based.asciidoc @@ -111,6 +111,8 @@ Choose the type of visualization you want to create, then use the editor to conf .. Select the visualization type you want to create. .. Select the data source you want to visualize. ++ +NOTE: There is no performance impact on the data source you select. For example, *Discover* saved searches perform the same as {data-sources}. . Add the <> you want to visualize using the editor, then click *Update*. + From 1cb82f5754e525d56f5ecbaa4c70f7f19e5f793c Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 8 Mar 2022 17:01:09 -0500 Subject: [PATCH 084/140] skip failing test suite (#126949) --- .../alerting/builtin_alert_types/index_threshold/alert.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts index ba2a2cb8fdf47..0c27868e3fbb6 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts @@ -35,7 +35,8 @@ export default function ruleTests({ getService }: FtrProviderContext) { const esTestIndexTool = new ESTestIndexTool(es, retry); const esTestIndexToolOutput = new ESTestIndexTool(es, retry, ES_TEST_OUTPUT_INDEX_NAME); - describe('rule', async () => { + // Failing: See https://github.com/elastic/kibana/issues/126949 + describe.skip('rule', async () => { let endDate: string; let connectorId: string; const objectRemover = new ObjectRemover(supertest); From 8c075c50d5aff8952d7fd20f815182d13e95c07f Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 8 Mar 2022 18:29:34 -0500 Subject: [PATCH 085/140] Increase timeout for Jest integration tests (#127220) Signed-off-by: Tyler Smalley --- .buildkite/pipelines/es_snapshots/verify.yml | 2 +- .buildkite/pipelines/hourly.yml | 4 ++-- .buildkite/pipelines/pull_request/base.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index 58908d1578bb5..18f3440b4acf7 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -67,7 +67,7 @@ steps: - command: .buildkite/scripts/steps/test/jest_integration.sh label: 'Jest Integration Tests' - parallelism: 2 + parallelism: 3 agents: queue: n2-4 timeout_in_minutes: 120 diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 78c57ff3bd128..a236f9c37b313 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -129,10 +129,10 @@ steps: - command: .buildkite/scripts/steps/test/jest_integration.sh label: 'Jest Integration Tests' - parallelism: 2 + parallelism: 3 agents: queue: n2-4 - timeout_in_minutes: 90 + timeout_in_minutes: 120 key: jest-integration - command: .buildkite/scripts/steps/test/api_integration.sh diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 3117ba98078d9..894f4c4368a3d 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -129,10 +129,10 @@ steps: - command: .buildkite/scripts/steps/test/jest_integration.sh label: 'Jest Integration Tests' - parallelism: 2 + parallelism: 3 agents: queue: n2-4 - timeout_in_minutes: 90 + timeout_in_minutes: 120 key: jest-integration - command: .buildkite/scripts/steps/test/api_integration.sh From 81eec7c36091d7e5fdcc348554366eacf443eec8 Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Tue, 8 Mar 2022 18:30:41 -0500 Subject: [PATCH 086/140] Made fix to broken test. Deleted all existing pipelines before test starts. FLAKY: https://github.com/elastic/kibana/issues/118593 (#127102) --- .../functional/apps/ingest_pipelines/ingest_pipelines.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts b/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts index 31d165336bd8a..baae09f47c530 100644 --- a/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts +++ b/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { IngestDeletePipelineRequest } from '@elastic/elasticsearch/lib/api/types'; import expect from '@kbn/expect'; import path from 'path'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -25,11 +26,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const es = getService('es'); const security = getService('security'); - // FLAKY: https://github.com/elastic/kibana/issues/118593 - describe.skip('Ingest Pipelines', function () { + describe('Ingest Pipelines', function () { this.tags('smoke'); before(async () => { await security.testUser.setRoles(['ingest_pipelines_user']); + // Delete all existing pipelines + await es.ingest.deletePipeline({ id: '*' } as IngestDeletePipelineRequest); await pageObjects.common.navigateToApp('ingestPipelines'); }); From 792366706700f85e332a7992ed406ade9e200da2 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 8 Mar 2022 17:34:04 -0600 Subject: [PATCH 087/140] [build] Up compression quality (#127064) * [build] Up compression quality * update snapshot --- packages/kbn-babel-preset/node_preset.js | 2 +- packages/kbn-babel-preset/webpack_preset.js | 2 +- .../basic_optimization.test.ts.snap | 147 ++++++++---------- .../src/worker/webpack.config.ts | 5 +- .../generate_packages_optimized_assets.ts | 15 +- 5 files changed, 87 insertions(+), 84 deletions(-) diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index 1c74d67716331..b69c59f5e2cc5 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -31,7 +31,7 @@ module.exports = (_, options = {}) => { // Because of that we should use for that value the same version we install // in the package.json in order to have the same polyfills between the environment // and the tests - corejs: '3.2.1', + corejs: '3.21.1', bugfixes: true, ...(options['@babel/preset-env'] || {}), diff --git a/packages/kbn-babel-preset/webpack_preset.js b/packages/kbn-babel-preset/webpack_preset.js index ea49c406d50f8..398124b5e9f71 100644 --- a/packages/kbn-babel-preset/webpack_preset.js +++ b/packages/kbn-babel-preset/webpack_preset.js @@ -18,7 +18,7 @@ module.exports = () => { modules: false, // Please read the explanation for this // in node_preset.js - corejs: '3.2.1', + corejs: '3.21.1', bugfixes: true, }, ], diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index ddcdd980153fb..52a183bc04b45 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -214,14 +214,7 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` function (e, n, t) { \\"use strict\\"; var r, - o = function () { - return ( - void 0 === r && - (r = Boolean(window && document && document.all && !window.atob)), - r - ); - }, - i = (function () { + o = (function () { var e = {}; return function (n) { if (void 0 === e[n]) { @@ -240,37 +233,37 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` return e[n]; }; })(), - a = []; + i = []; function c(e) { - for (var n = -1, t = 0; t < a.length; t++) - if (a[t].identifier === e) { + for (var n = -1, t = 0; t < i.length; t++) + if (i[t].identifier === e) { n = t; break; } return n; } - function u(e, n) { + function a(e, n) { for (var t = {}, r = [], o = 0; o < e.length; o++) { - var i = e[o], - u = n.base ? i[0] + n.base : i[0], + var a = e[o], + u = n.base ? a[0] + n.base : a[0], s = t[u] || 0, f = \\"\\".concat(u, \\" \\").concat(s); t[u] = s + 1; var l = c(f), - d = { css: i[1], media: i[2], sourceMap: i[3] }; + d = { css: a[1], media: a[2], sourceMap: a[3] }; -1 !== l - ? (a[l].references++, a[l].updater(d)) - : a.push({ identifier: f, updater: h(d, n), references: 1 }), + ? (i[l].references++, i[l].updater(d)) + : i.push({ identifier: f, updater: v(d, n), references: 1 }), r.push(f); } return r; } - function s(e) { + function u(e) { var n = document.createElement(\\"style\\"), r = e.attributes || {}; if (void 0 === r.nonce) { - var o = t.nc; - o && (r.nonce = o); + var i = t.nc; + i && (r.nonce = i); } if ( (Object.keys(r).forEach(function (e) { @@ -280,36 +273,36 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` ) e.insert(n); else { - var a = i(e.insert || \\"head\\"); - if (!a) + var c = o(e.insert || \\"head\\"); + if (!c) throw new Error( \\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\\" ); - a.appendChild(n); + c.appendChild(n); } return n; } - var f, - l = - ((f = []), + var s, + f = + ((s = []), function (e, n) { - return (f[e] = n), f.filter(Boolean).join(\\"\\\\n\\"); + return (s[e] = n), s.filter(Boolean).join(\\"\\\\n\\"); }); - function d(e, n, t, r) { + function l(e, n, t, r) { var o = t ? \\"\\" : r.media ? \\"@media \\".concat(r.media, \\" {\\").concat(r.css, \\"}\\") : r.css; - if (e.styleSheet) e.styleSheet.cssText = l(n, o); + if (e.styleSheet) e.styleSheet.cssText = f(n, o); else { var i = document.createTextNode(o), - a = e.childNodes; - a[n] && e.removeChild(a[n]), - a.length ? e.insertBefore(i, a[n]) : e.appendChild(i); + c = e.childNodes; + c[n] && e.removeChild(c[n]), + c.length ? e.insertBefore(i, c[n]) : e.appendChild(i); } } - function p(e, n, t) { + function d(e, n, t) { var r = t.css, o = t.media, i = t.sourceMap; @@ -329,18 +322,18 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` e.appendChild(document.createTextNode(r)); } } - var v = null, + var p = null, b = 0; - function h(e, n) { + function v(e, n) { var t, r, o; if (n.singleton) { var i = b++; - (t = v || (v = s(n))), - (r = d.bind(null, t, i, !1)), - (o = d.bind(null, t, i, !0)); + (t = p || (p = u(n))), + (r = l.bind(null, t, i, !1)), + (o = l.bind(null, t, i, !0)); } else - (t = s(n)), - (r = p.bind(null, t, n)), + (t = u(n)), + (r = d.bind(null, t, n)), (o = function () { !(function (e) { if (null === e.parentNode) return !1; @@ -365,8 +358,11 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` e.exports = function (e, n) { (n = n || {}).singleton || \\"boolean\\" == typeof n.singleton || - (n.singleton = o()); - var t = u((e = e || []), n); + (n.singleton = + (void 0 === r && + (r = Boolean(window && document && document.all && !window.atob)), + r)); + var t = a((e = e || []), n); return function (e) { if ( ((e = e || []), @@ -374,13 +370,13 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` ) { for (var r = 0; r < t.length; r++) { var o = c(t[r]); - a[o].references--; + i[o].references--; } - for (var i = u(e, n), s = 0; s < t.length; s++) { + for (var u = a(e, n), s = 0; s < t.length; s++) { var f = c(t[s]); - 0 === a[f].references && (a[f].updater(), a.splice(f, 1)); + 0 === i[f].references && (i[f].updater(), i.splice(f, 1)); } - t = i; + t = u; } }; }; @@ -393,27 +389,29 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` (n.toString = function () { return this.map(function (n) { var t = (function (e, n) { - var t = e[1] || \\"\\", - r = e[3]; - if (!r) return t; + var t, + r, + o, + i = e[1] || \\"\\", + c = e[3]; + if (!c) return i; if (n && \\"function\\" == typeof btoa) { - var o = - ((a = r), - (c = btoa(unescape(encodeURIComponent(JSON.stringify(a))))), - (u = + var a = + ((t = c), + (r = btoa(unescape(encodeURIComponent(JSON.stringify(t))))), + (o = \\"sourceMappingURL=data:application/json;charset=utf-8;base64,\\".concat( - c + r )), - \\"/*# \\".concat(u, \\" */\\")), - i = r.sources.map(function (e) { + \\"/*# \\".concat(o, \\" */\\")), + u = c.sources.map(function (e) { return \\"/*# sourceURL=\\" - .concat(r.sourceRoot || \\"\\") + .concat(c.sourceRoot || \\"\\") .concat(e, \\" */\\"); }); - return [t].concat(i).concat([o]).join(\\"\\\\n\\"); + return [i].concat(u).concat([a]).join(\\"\\\\n\\"); } - var a, c, u; - return [t].join(\\"\\\\n\\"); + return [i].join(\\"\\\\n\\"); })(n, e); return n[2] ? \\"@media \\".concat(n[2], \\" {\\").concat(t, \\"}\\") : t; }).join(\\"\\"); @@ -423,11 +421,11 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` var o = {}; if (r) for (var i = 0; i < this.length; i++) { - var a = this[i][0]; - null != a && (o[a] = !0); + var c = this[i][0]; + null != c && (o[c] = !0); } - for (var c = 0; c < e.length; c++) { - var u = [].concat(e[c]); + for (var a = 0; a < e.length; a++) { + var u = [].concat(e[a]); (r && o[u[0]]) || (t && (u[2] @@ -464,9 +462,7 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` o = t(7); \\"string\\" == typeof (o = o.__esModule ? o.default : o) && (o = [[e.i, o, \\"\\"]]); - var i = { insert: \\"head\\", singleton: !1 }; - r(o, i); - e.exports = o.locals || {}; + r(o, { insert: \\"head\\", singleton: !1 }), (e.exports = o.locals || {}); }, function (e, n, t) { (n = t(1)(!1)).push([ @@ -481,9 +477,7 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` o = t(9); \\"string\\" == typeof (o = o.__esModule ? o.default : o) && (o = [[e.i, o, \\"\\"]]); - var i = { insert: \\"head\\", singleton: !1 }; - r(o, i); - e.exports = o.locals || {}; + r(o, { insert: \\"head\\", singleton: !1 }), (e.exports = o.locals || {}); }, function (e, n, t) { (n = t(1)(!1)).push([ @@ -506,9 +500,7 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` o = t(12); \\"string\\" == typeof (o = o.__esModule ? o.default : o) && (o = [[e.i, o, \\"\\"]]); - var i = { insert: \\"head\\", singleton: !1 }; - r(o, i); - e.exports = o.locals || {}; + r(o, { insert: \\"head\\", singleton: !1 }), (e.exports = o.locals || {}); }, function (e, n, t) { (n = t(1)(!1)).push([e.i, \\"body{color:green}\\\\n\\", \\"\\"]), (e.exports = n); @@ -518,9 +510,7 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` o = t(14); \\"string\\" == typeof (o = o.__esModule ? o.default : o) && (o = [[e.i, o, \\"\\"]]); - var i = { insert: \\"head\\", singleton: !1 }; - r(o, i); - e.exports = o.locals || {}; + r(o, { insert: \\"head\\", singleton: !1 }), (e.exports = o.locals || {}); }, function (e, n, t) { (n = t(1)(!1)).push([e.i, \\"body{color:green}\\\\n\\", \\"\\"]), (e.exports = n); @@ -533,8 +523,9 @@ exports[`prepares assets for distribution: bar bundle 1`] = ` }), t.d(n, \\"fooLibFn\\", function () { return r.fooLibFn; - }); - t(5), t(10); + }), + t(5), + t(10); var r = t(2); function o() { return \\"bar\\"; diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 9454456a35c9a..fe4b04548244a 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -266,6 +266,9 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: filename: '[path].br', test: /\.(js|css)$/, cache: false, + compressionOptions: { + level: 11, + }, }), new CompressionPlugin({ algorithm: 'gzip', @@ -283,7 +286,7 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: extractComments: false, parallel: false, terserOptions: { - compress: true, + compress: { passes: 2 }, keep_classnames: true, mangle: true, }, diff --git a/src/dev/build/tasks/generate_packages_optimized_assets.ts b/src/dev/build/tasks/generate_packages_optimized_assets.ts index 982aedb9cfa2d..c8f215ad7632b 100644 --- a/src/dev/build/tasks/generate_packages_optimized_assets.ts +++ b/src/dev/build/tasks/generate_packages_optimized_assets.ts @@ -24,6 +24,7 @@ import terser from 'terser'; import vfs from 'vinyl-fs'; import globby from 'globby'; import del from 'del'; +import zlib from 'zlib'; import { Task, write } from '../lib'; @@ -55,21 +56,29 @@ async function optimizeAssets(log: ToolingLog, assetDir: string) { log.debug('Minify JS'); await asyncPipeline( vfs.src(['**/*.js'], { cwd: assetDir }), - gulpTerser({ compress: true, mangle: true }, terser.minify), + gulpTerser({ compress: { passes: 2 }, mangle: true }, terser.minify), vfs.dest(assetDir) ); log.debug('Brotli compress'); await asyncPipeline( vfs.src(['**/*.{js,css}'], { cwd: assetDir }), - gulpBrotli(), + gulpBrotli({ + params: { + [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY, + }, + }), vfs.dest(assetDir) ); log.debug('GZip compress'); await asyncPipeline( vfs.src(['**/*.{js,css}'], { cwd: assetDir }), - gulpGzip(), + gulpGzip({ + gzipOptions: { + level: 9, + }, + }), vfs.dest(assetDir) ); } finally { From ad0eb607723ab89b6c70e8e0b48932e7e5c61aab Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 8 Mar 2022 17:04:45 -0800 Subject: [PATCH 088/140] [kbn/generate] add basic package generator (#127095) --- .eslintignore | 1 + package.json | 4 + packages/BUILD.bazel | 278 +-- packages/kbn-dev-utils/BUILD.bazel | 7 +- .../sort_package_json/package.json | 4 + packages/kbn-dev-utils/src/index.ts | 1 + .../kbn-dev-utils/src/sort_package_json.ts | 43 + packages/kbn-generate/BUILD.bazel | 110 + packages/kbn-generate/README.md | 7 + packages/kbn-generate/jest.config.js | 13 + packages/kbn-generate/package.json | 10 + packages/kbn-generate/src/cli.ts | 184 ++ packages/kbn-generate/src/index.ts | 9 + .../templates/package/BUILD.bazel.ejs | 121 + .../templates/package/README.md.ejs | 3 + .../templates/package/jest.config.js.ejs | 5 + .../templates/package/package.json.ejs | 15 + .../templates/package/src/index.ts | 3 + .../templates/package/tsconfig.json.ejs | 17 + packages/kbn-generate/tsconfig.json | 17 + packages/kbn-packages/BUILD.bazel | 120 + packages/kbn-packages/README.md | 3 + packages/kbn-packages/jest.config.js | 13 + packages/kbn-packages/package.json | 10 + .../kbn-packages/src/discover_packages.ts | 28 + ...generate_packages_build_bazel_file.test.ts | 85 + .../src/generate_packages_build_bazel_file.ts | 49 + packages/kbn-packages/src/index.ts | 10 + packages/kbn-packages/src/package.test.ts | 38 + packages/kbn-packages/src/package.ts | 50 + packages/kbn-packages/tsconfig.json | 17 + packages/kbn-pm/dist/index.js | 2008 +++++++++-------- .../kbn-pm/src/utils/sort_package_json.ts | 35 +- .../src/lib/bazel_cli_config.ts | 2 +- scripts/generate.js | 11 + src/dev/file.ts | 14 +- src/dev/precommit_hook/casing_check_config.js | 5 +- yarn.lock | 16 + 38 files changed, 2200 insertions(+), 1166 deletions(-) create mode 100644 packages/kbn-dev-utils/sort_package_json/package.json create mode 100644 packages/kbn-dev-utils/src/sort_package_json.ts create mode 100644 packages/kbn-generate/BUILD.bazel create mode 100644 packages/kbn-generate/README.md create mode 100644 packages/kbn-generate/jest.config.js create mode 100644 packages/kbn-generate/package.json create mode 100644 packages/kbn-generate/src/cli.ts create mode 100644 packages/kbn-generate/src/index.ts create mode 100644 packages/kbn-generate/templates/package/BUILD.bazel.ejs create mode 100644 packages/kbn-generate/templates/package/README.md.ejs create mode 100644 packages/kbn-generate/templates/package/jest.config.js.ejs create mode 100644 packages/kbn-generate/templates/package/package.json.ejs create mode 100644 packages/kbn-generate/templates/package/src/index.ts create mode 100644 packages/kbn-generate/templates/package/tsconfig.json.ejs create mode 100644 packages/kbn-generate/tsconfig.json create mode 100644 packages/kbn-packages/BUILD.bazel create mode 100644 packages/kbn-packages/README.md create mode 100644 packages/kbn-packages/jest.config.js create mode 100644 packages/kbn-packages/package.json create mode 100644 packages/kbn-packages/src/discover_packages.ts create mode 100644 packages/kbn-packages/src/generate_packages_build_bazel_file.test.ts create mode 100644 packages/kbn-packages/src/generate_packages_build_bazel_file.ts create mode 100644 packages/kbn-packages/src/index.ts create mode 100644 packages/kbn-packages/src/package.test.ts create mode 100644 packages/kbn-packages/src/package.ts create mode 100644 packages/kbn-packages/tsconfig.json create mode 100644 scripts/generate.js diff --git a/.eslintignore b/.eslintignore index 8f1fcff422d0e..9b745756b6706 100644 --- a/.eslintignore +++ b/.eslintignore @@ -32,6 +32,7 @@ snapshots.js # package overrides /packages/elastic-eslint-config-kibana /packages/kbn-plugin-generator/template +/packages/kbn-generate/templates /packages/kbn-pm/dist /packages/kbn-test/src/functional_test_runner/__tests__/fixtures/ /packages/kbn-test/src/functional_test_runner/lib/config/__tests__/fixtures/ diff --git a/package.json b/package.json index 19d65e24879b3..55a33716e7614 100644 --- a/package.json +++ b/package.json @@ -469,7 +469,9 @@ "@kbn/eslint-import-resolver-kibana": "link:bazel-bin/packages/kbn-eslint-import-resolver-kibana", "@kbn/eslint-plugin-eslint": "link:bazel-bin/packages/kbn-eslint-plugin-eslint", "@kbn/expect": "link:bazel-bin/packages/kbn-expect", + "@kbn/generate": "link:bazel-bin/packages/kbn-generate", "@kbn/optimizer": "link:bazel-bin/packages/kbn-optimizer", + "@kbn/packages": "link:bazel-bin/packages/kbn-packages", "@kbn/plugin-generator": "link:bazel-bin/packages/kbn-plugin-generator", "@kbn/plugin-helpers": "link:bazel-bin/packages/kbn-plugin-helpers", "@kbn/pm": "link:packages/kbn-pm", @@ -585,6 +587,7 @@ "@types/kbn__es-archiver": "link:bazel-bin/packages/kbn-es-archiver/npm_module_types", "@types/kbn__es-query": "link:bazel-bin/packages/kbn-es-query/npm_module_types", "@types/kbn__field-types": "link:bazel-bin/packages/kbn-field-types/npm_module_types", + "@types/kbn__generate": "link:bazel-bin/packages/kbn-generate/npm_module_types", "@types/kbn__i18n": "link:bazel-bin/packages/kbn-i18n/npm_module_types", "@types/kbn__i18n-react": "link:bazel-bin/packages/kbn-i18n-react/npm_module_types", "@types/kbn__interpreter": "link:bazel-bin/packages/kbn-interpreter/npm_module_types", @@ -594,6 +597,7 @@ "@types/kbn__mapbox-gl": "link:bazel-bin/packages/kbn-mapbox-gl/npm_module_types", "@types/kbn__monaco": "link:bazel-bin/packages/kbn-monaco/npm_module_types", "@types/kbn__optimizer": "link:bazel-bin/packages/kbn-optimizer/npm_module_types", + "@types/kbn__packages": "link:bazel-bin/packages/kbn-packages/npm_module_types", "@types/kbn__plugin-generator": "link:bazel-bin/packages/kbn-plugin-generator/npm_module_types", "@types/kbn__plugin-helpers": "link:bazel-bin/packages/kbn-plugin-helpers/npm_module_types", "@types/kbn__react-field": "link:bazel-bin/packages/kbn-react-field/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 77ec3b0c17295..e73cf0a00c1d0 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -1,79 +1,87 @@ +################ +################ +## This file is automatically generated, to create a new package use `node scripts/generate package --help` +################ +################ + # It will build all declared code packages filegroup( name = "build_pkg_code", srcs = [ - "//packages/elastic-apm-synthtrace:build", - "//packages/elastic-datemath:build", - "//packages/elastic-eslint-config-kibana:build", - "//packages/elastic-safer-lodash-set:build", - "//packages/kbn-ace:build", - "//packages/kbn-alerts:build", - "//packages/kbn-analytics:build", - "//packages/kbn-apm-config-loader:build", - "//packages/kbn-apm-utils:build", - "//packages/kbn-babel-code-parser:build", - "//packages/kbn-babel-preset:build", - "//packages/kbn-cli-dev-mode:build", - "//packages/kbn-config:build", - "//packages/kbn-config-schema:build", - "//packages/kbn-crypto:build", - "//packages/kbn-dev-utils:build", - "//packages/kbn-doc-links:build", - "//packages/kbn-docs-utils:build", - "//packages/kbn-es:build", - "//packages/kbn-es-archiver:build", - "//packages/kbn-es-query:build", - "//packages/kbn-eslint-import-resolver-kibana:build", - "//packages/kbn-eslint-plugin-eslint:build", - "//packages/kbn-expect:build", - "//packages/kbn-field-types:build", - "//packages/kbn-flot-charts:build", - "//packages/kbn-i18n:build", - "//packages/kbn-i18n-react:build", - "//packages/kbn-interpreter:build", - "//packages/kbn-io-ts-utils:build", - "//packages/kbn-logging:build", - "//packages/kbn-logging-mocks:build", - "//packages/kbn-mapbox-gl:build", - "//packages/kbn-monaco:build", - "//packages/kbn-optimizer:build", - "//packages/kbn-plugin-generator:build", - "//packages/kbn-plugin-helpers:build", - "//packages/kbn-react-field:build", - "//packages/kbn-rule-data-utils:build", - "//packages/kbn-securitysolution-autocomplete:build", - "//packages/kbn-securitysolution-list-constants:build", - "//packages/kbn-securitysolution-io-ts-types:build", - "//packages/kbn-securitysolution-io-ts-alerting-types:build", - "//packages/kbn-securitysolution-io-ts-list-types:build", - "//packages/kbn-securitysolution-io-ts-utils:build", - "//packages/kbn-securitysolution-list-api:build", - "//packages/kbn-securitysolution-list-hooks:build", - "//packages/kbn-securitysolution-list-utils:build", - "//packages/kbn-securitysolution-rules:build", - "//packages/kbn-securitysolution-utils:build", - "//packages/kbn-securitysolution-es-utils:build", - "//packages/kbn-securitysolution-t-grid:build", - "//packages/kbn-securitysolution-hook-utils:build", - "//packages/kbn-server-http-tools:build", - "//packages/kbn-server-route-repository:build", - "//packages/kbn-spec-to-console:build", - "//packages/kbn-std:build", - "//packages/kbn-storybook:build", - "//packages/kbn-telemetry-tools:build", - "//packages/kbn-test:build", - "//packages/kbn-test-jest-helpers:build", - "//packages/kbn-test-subj-selector:build", - "//packages/kbn-timelion-grammar:build", - "//packages/kbn-tinymath:build", - "//packages/kbn-type-summarizer:build", - "//packages/kbn-typed-react-router-config:build", - "//packages/kbn-ui-framework:build", - "//packages/kbn-ui-shared-deps-npm:build", - "//packages/kbn-ui-shared-deps-src:build", - "//packages/kbn-ui-theme:build", - "//packages/kbn-utility-types:build", - "//packages/kbn-utils:build", + "//packages/elastic-apm-synthtrace:build", + "//packages/elastic-datemath:build", + "//packages/elastic-eslint-config-kibana:build", + "//packages/elastic-safer-lodash-set:build", + "//packages/kbn-ace:build", + "//packages/kbn-alerts:build", + "//packages/kbn-analytics:build", + "//packages/kbn-apm-config-loader:build", + "//packages/kbn-apm-utils:build", + "//packages/kbn-babel-code-parser:build", + "//packages/kbn-babel-preset:build", + "//packages/kbn-cli-dev-mode:build", + "//packages/kbn-config-schema:build", + "//packages/kbn-config:build", + "//packages/kbn-crypto:build", + "//packages/kbn-dev-utils:build", + "//packages/kbn-doc-links:build", + "//packages/kbn-docs-utils:build", + "//packages/kbn-es-archiver:build", + "//packages/kbn-es-query:build", + "//packages/kbn-es:build", + "//packages/kbn-eslint-import-resolver-kibana:build", + "//packages/kbn-eslint-plugin-eslint:build", + "//packages/kbn-expect:build", + "//packages/kbn-field-types:build", + "//packages/kbn-flot-charts:build", + "//packages/kbn-generate:build", + "//packages/kbn-i18n-react:build", + "//packages/kbn-i18n:build", + "//packages/kbn-interpreter:build", + "//packages/kbn-io-ts-utils:build", + "//packages/kbn-logging-mocks:build", + "//packages/kbn-logging:build", + "//packages/kbn-mapbox-gl:build", + "//packages/kbn-monaco:build", + "//packages/kbn-optimizer:build", + "//packages/kbn-packages:build", + "//packages/kbn-plugin-generator:build", + "//packages/kbn-plugin-helpers:build", + "//packages/kbn-react-field:build", + "//packages/kbn-rule-data-utils:build", + "//packages/kbn-securitysolution-autocomplete:build", + "//packages/kbn-securitysolution-es-utils:build", + "//packages/kbn-securitysolution-hook-utils:build", + "//packages/kbn-securitysolution-io-ts-alerting-types:build", + "//packages/kbn-securitysolution-io-ts-list-types:build", + "//packages/kbn-securitysolution-io-ts-types:build", + "//packages/kbn-securitysolution-io-ts-utils:build", + "//packages/kbn-securitysolution-list-api:build", + "//packages/kbn-securitysolution-list-constants:build", + "//packages/kbn-securitysolution-list-hooks:build", + "//packages/kbn-securitysolution-list-utils:build", + "//packages/kbn-securitysolution-rules:build", + "//packages/kbn-securitysolution-t-grid:build", + "//packages/kbn-securitysolution-utils:build", + "//packages/kbn-server-http-tools:build", + "//packages/kbn-server-route-repository:build", + "//packages/kbn-spec-to-console:build", + "//packages/kbn-std:build", + "//packages/kbn-storybook:build", + "//packages/kbn-telemetry-tools:build", + "//packages/kbn-test-jest-helpers:build", + "//packages/kbn-test-subj-selector:build", + "//packages/kbn-test:build", + "//packages/kbn-timelion-grammar:build", + "//packages/kbn-tinymath:build", + "//packages/kbn-type-summarizer:build", + "//packages/kbn-typed-react-router-config:build", + "//packages/kbn-ui-framework:build", + "//packages/kbn-ui-shared-deps-npm:build", + "//packages/kbn-ui-shared-deps-src:build", + "//packages/kbn-ui-theme:build", + "//packages/kbn-utility-types:build", + "//packages/kbn-utils:build", ], ) @@ -81,77 +89,77 @@ filegroup( filegroup( name = "build_pkg_types", srcs = [ - "//packages/elastic-apm-synthtrace:build_types", - "//packages/elastic-datemath:build_types", - "//packages/elastic-safer-lodash-set:build_types", - "//packages/kbn-ace:build_types", - "//packages/kbn-alerts:build_types", - "//packages/kbn-analytics:build_types", - "//packages/kbn-apm-config-loader:build_types", - "//packages/kbn-apm-utils:build_types", - "//packages/kbn-cli-dev-mode:build_types", - "//packages/kbn-config:build_types", - "//packages/kbn-config-schema:build_types", - "//packages/kbn-crypto:build_types", - "//packages/kbn-dev-utils:build_types", - "//packages/kbn-doc-links:build_types", - "//packages/kbn-docs-utils:build_types", - "//packages/kbn-es-archiver:build_types", - "//packages/kbn-es-query:build_types", - "//packages/kbn-field-types:build_types", - "//packages/kbn-i18n:build_types", - "//packages/kbn-i18n-react:build_types", - "//packages/kbn-interpreter:build_types", - "//packages/kbn-io-ts-utils:build_types", - "//packages/kbn-logging:build_types", - "//packages/kbn-logging-mocks:build_types", - "//packages/kbn-mapbox-gl:build_types", - "//packages/kbn-monaco:build_types", - "//packages/kbn-optimizer:build_types", - "//packages/kbn-plugin-generator:build_types", - "//packages/kbn-plugin-helpers:build_types", - "//packages/kbn-react-field:build_types", - "//packages/kbn-rule-data-utils:build_types", - "//packages/kbn-securitysolution-autocomplete:build_types", - "//packages/kbn-securitysolution-es-utils:build_types", - "//packages/kbn-securitysolution-hook-utils:build_types", - "//packages/kbn-securitysolution-io-ts-alerting-types:build_types", - "//packages/kbn-securitysolution-io-ts-list-types:build_types", - "//packages/kbn-securitysolution-io-ts-types:build_types", - "//packages/kbn-securitysolution-io-ts-utils:build_types", - "//packages/kbn-securitysolution-list-api:build_types", - "//packages/kbn-securitysolution-list-constants:build_types", - "//packages/kbn-securitysolution-list-hooks:build_types", - "//packages/kbn-securitysolution-list-utils:build_types", - "//packages/kbn-securitysolution-rules:build_types", - "//packages/kbn-securitysolution-t-grid:build_types", - "//packages/kbn-securitysolution-utils:build_types", - "//packages/kbn-server-http-tools:build_types", - "//packages/kbn-server-route-repository:build_types", - "//packages/kbn-std:build_types", - "//packages/kbn-storybook:build_types", - "//packages/kbn-telemetry-tools:build_types", - "//packages/kbn-test:build_types", - "//packages/kbn-test-jest-helpers:build_types", - "//packages/kbn-type-summarizer:build_types", - "//packages/kbn-typed-react-router-config:build_types", - "//packages/kbn-ui-shared-deps-npm:build_types", - "//packages/kbn-ui-shared-deps-src:build_types", - "//packages/kbn-ui-theme:build_types", - "//packages/kbn-utility-types:build_types", - "//packages/kbn-utils:build_types", + "//packages/elastic-apm-synthtrace:build_types", + "//packages/elastic-datemath:build_types", + "//packages/elastic-safer-lodash-set:build_types", + "//packages/kbn-ace:build_types", + "//packages/kbn-alerts:build_types", + "//packages/kbn-analytics:build_types", + "//packages/kbn-apm-config-loader:build_types", + "//packages/kbn-apm-utils:build_types", + "//packages/kbn-cli-dev-mode:build_types", + "//packages/kbn-config-schema:build_types", + "//packages/kbn-config:build_types", + "//packages/kbn-crypto:build_types", + "//packages/kbn-dev-utils:build_types", + "//packages/kbn-doc-links:build_types", + "//packages/kbn-docs-utils:build_types", + "//packages/kbn-es-archiver:build_types", + "//packages/kbn-es-query:build_types", + "//packages/kbn-field-types:build_types", + "//packages/kbn-generate:build_types", + "//packages/kbn-i18n-react:build_types", + "//packages/kbn-i18n:build_types", + "//packages/kbn-interpreter:build_types", + "//packages/kbn-io-ts-utils:build_types", + "//packages/kbn-logging-mocks:build_types", + "//packages/kbn-logging:build_types", + "//packages/kbn-mapbox-gl:build_types", + "//packages/kbn-monaco:build_types", + "//packages/kbn-optimizer:build_types", + "//packages/kbn-packages:build_types", + "//packages/kbn-plugin-generator:build_types", + "//packages/kbn-plugin-helpers:build_types", + "//packages/kbn-react-field:build_types", + "//packages/kbn-rule-data-utils:build_types", + "//packages/kbn-securitysolution-autocomplete:build_types", + "//packages/kbn-securitysolution-es-utils:build_types", + "//packages/kbn-securitysolution-hook-utils:build_types", + "//packages/kbn-securitysolution-io-ts-alerting-types:build_types", + "//packages/kbn-securitysolution-io-ts-list-types:build_types", + "//packages/kbn-securitysolution-io-ts-types:build_types", + "//packages/kbn-securitysolution-io-ts-utils:build_types", + "//packages/kbn-securitysolution-list-api:build_types", + "//packages/kbn-securitysolution-list-constants:build_types", + "//packages/kbn-securitysolution-list-hooks:build_types", + "//packages/kbn-securitysolution-list-utils:build_types", + "//packages/kbn-securitysolution-rules:build_types", + "//packages/kbn-securitysolution-t-grid:build_types", + "//packages/kbn-securitysolution-utils:build_types", + "//packages/kbn-server-http-tools:build_types", + "//packages/kbn-server-route-repository:build_types", + "//packages/kbn-std:build_types", + "//packages/kbn-storybook:build_types", + "//packages/kbn-telemetry-tools:build_types", + "//packages/kbn-test-jest-helpers:build_types", + "//packages/kbn-test:build_types", + "//packages/kbn-type-summarizer:build_types", + "//packages/kbn-typed-react-router-config:build_types", + "//packages/kbn-ui-shared-deps-npm:build_types", + "//packages/kbn-ui-shared-deps-src:build_types", + "//packages/kbn-ui-theme:build_types", + "//packages/kbn-utility-types:build_types", + "//packages/kbn-utils:build_types", ], ) - - # Grouping target to call all underlying packages build # targets so we can build them all at once # It will auto build all declared code packages and types packages filegroup( name = "build", srcs = [ - ":build_pkg_code", - ":build_pkg_types" + ":build_pkg_code", + ":build_pkg_types" ], ) diff --git a/packages/kbn-dev-utils/BUILD.bazel b/packages/kbn-dev-utils/BUILD.bazel index 7be527c65a06c..e18cdff0ec3ff 100644 --- a/packages/kbn-dev-utils/BUILD.bazel +++ b/packages/kbn-dev-utils/BUILD.bazel @@ -38,13 +38,14 @@ NPM_MODULE_EXTRA_FILES = [ "README.md", ":certs", "ci_stats_reporter/package.json", + "sort_package_json/package.json", "stdio/package.json", "tooling_log/package.json" ] RUNTIME_DEPS = [ - "//packages/kbn-utils", "//packages/kbn-std", + "//packages/kbn-utils", "@npm//@babel/core", "@npm//axios", "@npm//chalk", @@ -59,6 +60,7 @@ RUNTIME_DEPS = [ "@npm//normalize-path", "@npm//prettier", "@npm//rxjs", + "@npm//sort-package-json", "@npm//tar", "@npm//tree-kill", "@npm//vinyl", @@ -66,8 +68,8 @@ RUNTIME_DEPS = [ ] TYPES_DEPS = [ - "//packages/kbn-utils:npm_module_types", "//packages/kbn-std:npm_module_types", + "//packages/kbn-utils:npm_module_types", "@npm//@babel/parser", "@npm//@babel/types", "@npm//@types/babel__core", @@ -89,6 +91,7 @@ TYPES_DEPS = [ "@npm//exit-hook", "@npm//getopts", "@npm//rxjs", + "@npm//sort-package-json", "@npm//tree-kill", ] diff --git a/packages/kbn-dev-utils/sort_package_json/package.json b/packages/kbn-dev-utils/sort_package_json/package.json new file mode 100644 index 0000000000000..e075ec436de33 --- /dev/null +++ b/packages/kbn-dev-utils/sort_package_json/package.json @@ -0,0 +1,4 @@ +{ + "main": "../target_node/sort_package_json", + "types": "../target_types/sort_package_json" +} \ No newline at end of file diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index 9b207ad9e9966..fa40a5c37b09b 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -32,3 +32,4 @@ export * from './streams'; export * from './babel'; export * from './extract'; export * from './vscode_config'; +export * from './sort_package_json'; diff --git a/packages/kbn-dev-utils/src/sort_package_json.ts b/packages/kbn-dev-utils/src/sort_package_json.ts new file mode 100644 index 0000000000000..9873244eb3676 --- /dev/null +++ b/packages/kbn-dev-utils/src/sort_package_json.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import sorter from 'sort-package-json'; + +export function sortPackageJson(json: string) { + return ( + JSON.stringify( + sorter(JSON.parse(json), { + // top level keys in the order they were written when this was implemented + sortOrder: [ + 'name', + 'description', + 'keywords', + 'private', + 'version', + 'branch', + 'main', + 'browser', + 'types', + 'tsdocMetadata', + 'build', + 'homepage', + 'bugs', + 'license', + 'kibana', + 'author', + 'scripts', + 'repository', + 'engines', + 'resolutions', + ], + }), + null, + 2 + ) + '\n' + ); +} diff --git a/packages/kbn-generate/BUILD.bazel b/packages/kbn-generate/BUILD.bazel new file mode 100644 index 0000000000000..b6238bbca3a53 --- /dev/null +++ b/packages/kbn-generate/BUILD.bazel @@ -0,0 +1,110 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_BASE_NAME = "kbn-generate" +PKG_REQUIRE_NAME = "@kbn/generate" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*" + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = glob(["templates/**/*"]) + [ + "package.json", +] + +RUNTIME_DEPS = [ + "//packages/kbn-dev-utils", + "//packages/kbn-packages", + "//packages/kbn-utils", + "@npm//ejs", + "@npm//normalize-path", +] + +TYPES_DEPS = [ + "//packages/kbn-dev-utils:npm_module_types", + "//packages/kbn-packages:npm_module_types", + "//packages/kbn-utils:npm_module_types", + "@npm//ejs", + "@npm//normalize-path", + "@npm//@types/ejs", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [ + ":npm_module_types", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-generate/README.md b/packages/kbn-generate/README.md new file mode 100644 index 0000000000000..8257d61ade720 --- /dev/null +++ b/packages/kbn-generate/README.md @@ -0,0 +1,7 @@ +# @kbn/generate + +Provides a CLI for generating different components within Kibana + +## `node scripts/generate package` + +Generate a Kibana package, run `node scripts/generate package --help` for details about options. \ No newline at end of file diff --git a/packages/kbn-generate/jest.config.js b/packages/kbn-generate/jest.config.js new file mode 100644 index 0000000000000..123b208595ef7 --- /dev/null +++ b/packages/kbn-generate/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-generate'], +}; diff --git a/packages/kbn-generate/package.json b/packages/kbn-generate/package.json new file mode 100644 index 0000000000000..aacf8c6aab817 --- /dev/null +++ b/packages/kbn-generate/package.json @@ -0,0 +1,10 @@ +{ + "name": "@kbn/generate", + "version": "1.0.0", + "private": true, + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target_node/index.js", + "kibana": { + "devOnly": true + } +} \ No newline at end of file diff --git a/packages/kbn-generate/src/cli.ts b/packages/kbn-generate/src/cli.ts new file mode 100644 index 0000000000000..25606faf64e88 --- /dev/null +++ b/packages/kbn-generate/src/cli.ts @@ -0,0 +1,184 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Fsp from 'fs/promises'; +import Path from 'path'; + +import Ejs from 'ejs'; +import globby from 'globby'; +import { REPO_ROOT } from '@kbn/utils'; +import { + RunWithCommands, + createFlagError, + createFailError, + isFailError, + sortPackageJson, +} from '@kbn/dev-utils'; +import { discoverPackages, generatePackagesBuildBazelFile } from '@kbn/packages'; +import normalizePath from 'normalize-path'; + +const ROOT_PKG_DIR = Path.resolve(REPO_ROOT, 'packages'); +const TEMPLATE_DIR = Path.resolve(__dirname, '../templates/package'); + +const jsonHelper = (arg: any) => JSON.stringify(arg, null, 2); +const jsHelper = (arg: string) => { + if (typeof arg !== 'string') { + throw new Error('js() only supports strings right now'); + } + + const hasSingle = arg.includes(`'`); + const hasBacktick = arg.includes('`'); + + if (!hasSingle) { + return `'${arg}'`; + } + + if (!hasBacktick) { + return `\`${arg}\``; + } + + return `'${arg.replaceAll(`'`, `\\'`)}'`; +}; + +export function runGenerateCli() { + new RunWithCommands({ + description: 'Run generators for different components in Kibana', + }) + .command({ + name: 'package', + description: 'Generate a basic package', + usage: 'node scripts/generate package [name]', + flags: { + boolean: ['web', 'force', 'dev'], + string: ['dir'], + help: ` + --dev Generate a package which is intended for dev-only use and can access things like devDependencies + --web Build webpack-compatible version of sources for this package + --dir Directory where this package will live, defaults to [./packages] + --force If the packageDir already exists, delete it before generation + `, + }, + async run({ log, flags }) { + const [name] = flags._; + if (!name) { + throw createFlagError(`missing package name`); + } + if (!name.startsWith('@kbn/')) { + throw createFlagError(`package name must start with @kbn/`); + } + + const typePkgName = `@types/${name.slice(1).replace('/', '__')}`; + const web = !!flags.web; + const dev = !!flags.dev; + + const containingDir = flags.dir ? Path.resolve(`${flags.dir}`) : ROOT_PKG_DIR; + const packageDir = Path.resolve(containingDir, name.slice(1).replace('/', '-')); + const repoRelativeDir = normalizePath(Path.relative(REPO_ROOT, packageDir)); + + try { + await Fsp.readdir(packageDir); + if (!!flags.force) { + await Fsp.rm(packageDir, { recursive: true }); + log.warning('deleted existing package at', packageDir); + } else { + throw createFailError( + `Package dir [${packageDir}] already exists, either choose a new package name, or pass --force to delete the package and regenerate it` + ); + } + } catch (error) { + if (isFailError(error)) { + throw error; + } + } + + const templateFiles = await globby('**/*', { + cwd: TEMPLATE_DIR, + absolute: false, + dot: true, + onlyFiles: true, + }); + + await Fsp.mkdir(packageDir, { recursive: true }); + + for (const rel of templateFiles) { + const destDir = Path.resolve(packageDir, Path.dirname(rel)); + + await Fsp.mkdir(destDir, { recursive: true }); + + if (Path.basename(rel) === '.empty') { + log.debug('created dir', destDir); + // ignore .empty files in the template, just create the directory + continue; + } + + const ejs = !!rel.endsWith('.ejs'); + const src = Path.resolve(TEMPLATE_DIR, rel); + const dest = Path.resolve(packageDir, ejs ? rel.slice(0, -4) : rel); + + if (!ejs) { + await Fsp.copyFile(src, dest); + log.debug('copied', rel); + continue; + } + + const vars = { + pkg: { + name, + web, + dev, + directoryName: Path.basename(repoRelativeDir), + repoRelativeDir, + }, + + // helpers + json: jsonHelper, + js: jsHelper, + relativePathTo: (rootRelativePath: string) => { + return Path.relative(Path.dirname(dest), Path.resolve(REPO_ROOT, rootRelativePath)); + }, + }; + + log.verbose('rendering', src, 'with variables', vars); + let content = await Ejs.renderFile(src, vars); + + if (Path.basename(dest) === 'package.json') { + content = sortPackageJson(content); + } + + await Fsp.writeFile(dest, content); + log.debug('rendered', rel); + } + + log.info('Wrote plugin files to', packageDir); + + const packageJsonPath = Path.resolve(REPO_ROOT, 'package.json'); + const packageJson = JSON.parse(await Fsp.readFile(packageJsonPath, 'utf8')); + + const [addDeps, removeDeps] = dev + ? [packageJson.devDependencies, packageJson.dependencies] + : [packageJson.dependencies, packageJson.devDependencies]; + + addDeps[name] = `link:bazel-bin/${repoRelativeDir}`; + addDeps[typePkgName] = `link:bazel-bin/${repoRelativeDir}/npm_module_types`; + delete removeDeps[name]; + delete removeDeps[typePkgName]; + + await Fsp.writeFile(packageJsonPath, sortPackageJson(JSON.stringify(packageJson))); + log.info('Updated package.json file'); + + await Fsp.writeFile( + Path.resolve(REPO_ROOT, 'packages/BUILD.bazel'), + generatePackagesBuildBazelFile(await discoverPackages()) + ); + log.info('Updated packages/BUILD.bazel'); + + log.success(`Generated ${name}! Please bootstrap to make sure it works.`); + }, + }) + .execute(); +} diff --git a/packages/kbn-generate/src/index.ts b/packages/kbn-generate/src/index.ts new file mode 100644 index 0000000000000..7ee97ec9aa05d --- /dev/null +++ b/packages/kbn-generate/src/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './cli'; diff --git a/packages/kbn-generate/templates/package/BUILD.bazel.ejs b/packages/kbn-generate/templates/package/BUILD.bazel.ejs new file mode 100644 index 0000000000000..a74f0c1f42dc5 --- /dev/null +++ b/packages/kbn-generate/templates/package/BUILD.bazel.ejs @@ -0,0 +1,121 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = <%- json(pkg.directoryName) %> +PKG_REQUIRE_NAME = <%- json(pkg.name) %> + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "//@npm//@types/node", + "//@npm//@types/jest", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) +<% if (pkg.web) { %> +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, +) +<% } %> +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + <%- pkg.web ? '[":target_node", ":target_web"]' : '[":target_node"]' %>, + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-generate/templates/package/README.md.ejs b/packages/kbn-generate/templates/package/README.md.ejs new file mode 100644 index 0000000000000..769f536648a6d --- /dev/null +++ b/packages/kbn-generate/templates/package/README.md.ejs @@ -0,0 +1,3 @@ +# <%- pkg.name %> + +Empty package generated by @kbn/generate diff --git a/packages/kbn-generate/templates/package/jest.config.js.ejs b/packages/kbn-generate/templates/package/jest.config.js.ejs new file mode 100644 index 0000000000000..1846d6a8f96f5 --- /dev/null +++ b/packages/kbn-generate/templates/package/jest.config.js.ejs @@ -0,0 +1,5 @@ +module.exports = { + preset: <%- js(pkg.web ? '@kbn/test' : '@kbn/test/jest_node') %>, + rootDir: '../..', + roots: [<%- js(`/${pkg.repoRelativeDir}`) %>], +}; diff --git a/packages/kbn-generate/templates/package/package.json.ejs b/packages/kbn-generate/templates/package/package.json.ejs new file mode 100644 index 0000000000000..93b2813daffa1 --- /dev/null +++ b/packages/kbn-generate/templates/package/package.json.ejs @@ -0,0 +1,15 @@ +{ + "name": <%- json(pkg.name) %>, + "version": "1.0.0", + "private": true, + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target_node/index.js" + <%_ if (pkg.web) { %>, + "browser": "./target_web/index.js" + <%_ } %> + <% if (pkg.dev) { %>, + "kibana": { + "devOnly": true + } + <% } %> +} diff --git a/packages/kbn-generate/templates/package/src/index.ts b/packages/kbn-generate/templates/package/src/index.ts new file mode 100644 index 0000000000000..7657a38ed92e4 --- /dev/null +++ b/packages/kbn-generate/templates/package/src/index.ts @@ -0,0 +1,3 @@ +export function foo() { + return 'hello world'; +} diff --git a/packages/kbn-generate/templates/package/tsconfig.json.ejs b/packages/kbn-generate/templates/package/tsconfig.json.ejs new file mode 100644 index 0000000000000..55edb60925e31 --- /dev/null +++ b/packages/kbn-generate/templates/package/tsconfig.json.ejs @@ -0,0 +1,17 @@ +{ + "extends": "<%- relativePathTo("tsconfig.bazel.json") %>", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-generate/tsconfig.json b/packages/kbn-generate/tsconfig.json new file mode 100644 index 0000000000000..a8cfc2cceb08b --- /dev/null +++ b/packages/kbn-generate/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-packages/BUILD.bazel b/packages/kbn-packages/BUILD.bazel new file mode 100644 index 0000000000000..56870853c940c --- /dev/null +++ b/packages/kbn-packages/BUILD.bazel @@ -0,0 +1,120 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "kbn-packages" +PKG_REQUIRE_NAME = "@kbn/packages" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "//packages/kbn-utils", + "//packages/kbn-std", + "@npm//globby", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS: +# eg. "@npm//@types/babel__core" +TYPES_DEPS = [ + "//packages/kbn-utils:npm_module_types", + "//packages/kbn-std:npm_module_types", + "@npm//globby", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-packages/README.md b/packages/kbn-packages/README.md new file mode 100644 index 0000000000000..c9153338a2858 --- /dev/null +++ b/packages/kbn-packages/README.md @@ -0,0 +1,3 @@ +# @kbn/packages + +Empty package generated by @kbn/generate diff --git a/packages/kbn-packages/jest.config.js b/packages/kbn-packages/jest.config.js new file mode 100644 index 0000000000000..2adb8d01fa4fa --- /dev/null +++ b/packages/kbn-packages/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-packages'], +}; diff --git a/packages/kbn-packages/package.json b/packages/kbn-packages/package.json new file mode 100644 index 0000000000000..48fd0d95fdd88 --- /dev/null +++ b/packages/kbn-packages/package.json @@ -0,0 +1,10 @@ +{ + "name": "@kbn/packages", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0", + "kibana": { + "devOnly": true + } +} diff --git a/packages/kbn-packages/src/discover_packages.ts b/packages/kbn-packages/src/discover_packages.ts new file mode 100644 index 0000000000000..0571cda5658eb --- /dev/null +++ b/packages/kbn-packages/src/discover_packages.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; + +import globby from 'globby'; +import { REPO_ROOT } from '@kbn/utils'; +import { asyncMapWithLimit } from '@kbn/std'; + +import { Package } from './package'; + +export async function discoverPackages() { + const packageJsons = globby.sync('*/package.json', { + cwd: Path.resolve(REPO_ROOT, 'packages'), + absolute: true, + }); + + return await asyncMapWithLimit( + packageJsons.sort((a, b) => a.localeCompare(b)), + 10, + (path) => Package.fromDir(Path.dirname(path)) + ); +} diff --git a/packages/kbn-packages/src/generate_packages_build_bazel_file.test.ts b/packages/kbn-packages/src/generate_packages_build_bazel_file.test.ts new file mode 100644 index 0000000000000..d871aecda4d5d --- /dev/null +++ b/packages/kbn-packages/src/generate_packages_build_bazel_file.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { generatePackagesBuildBazelFile } from './generate_packages_build_bazel_file'; + +import { Package } from './package'; + +it('produces a valid BUILD.bazel file', () => { + const packages = [ + new Package( + 'foo', + {}, + ` + rule( + name = "build" + ) + rule( + name = "build_types" + ) + ` + ), + new Package( + 'bar', + {}, + ` + rule( + name= "build_types" + ) + ` + ), + new Package( + 'bar', + {}, + ` + rule( + name ="build" + ) + ` + ), + new Package('bar', {}), + ]; + + expect(generatePackagesBuildBazelFile(packages)).toMatchInlineSnapshot(` + "################ + ################ + ## This file is automatically generated, to create a new package use \`node scripts/generate package --help\` + ################ + ################ + + # It will build all declared code packages + filegroup( + name = \\"build_pkg_code\\", + srcs = [ + \\"//foo:build\\", + \\"//bar:build\\", + ], + ) + + # It will build all declared package types + filegroup( + name = \\"build_pkg_types\\", + srcs = [ + \\"//foo:build_types\\", + \\"//bar:build_types\\", + ], + ) + + # Grouping target to call all underlying packages build + # targets so we can build them all at once + # It will auto build all declared code packages and types packages + filegroup( + name = \\"build\\", + srcs = [ + \\":build_pkg_code\\", + \\":build_pkg_types\\" + ], + ) + " + `); +}); diff --git a/packages/kbn-packages/src/generate_packages_build_bazel_file.ts b/packages/kbn-packages/src/generate_packages_build_bazel_file.ts new file mode 100644 index 0000000000000..45afea8cde706 --- /dev/null +++ b/packages/kbn-packages/src/generate_packages_build_bazel_file.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Package } from './package'; + +export function generatePackagesBuildBazelFile(packages: Package[]) { + return `################ +################ +## This file is automatically generated, to create a new package use \`node scripts/generate package --help\` +################ +################ + +# It will build all declared code packages +filegroup( + name = "build_pkg_code", + srcs = [ +${packages + .flatMap((p) => (p.hasBuildRule() ? ` "//${p.repoRelativeDir}:build",` : [])) + .join('\n')} + ], +) + +# It will build all declared package types +filegroup( + name = "build_pkg_types", + srcs = [ +${packages + .flatMap((p) => (p.hasBuildTypesRule() ? ` "//${p.repoRelativeDir}:build_types",` : [])) + .join('\n')} + ], +) + +# Grouping target to call all underlying packages build +# targets so we can build them all at once +# It will auto build all declared code packages and types packages +filegroup( + name = "build", + srcs = [ + ":build_pkg_code", + ":build_pkg_types" + ], +) +`; +} diff --git a/packages/kbn-packages/src/index.ts b/packages/kbn-packages/src/index.ts new file mode 100644 index 0000000000000..bb05cff69c767 --- /dev/null +++ b/packages/kbn-packages/src/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './discover_packages'; +export * from './generate_packages_build_bazel_file'; diff --git a/packages/kbn-packages/src/package.test.ts b/packages/kbn-packages/src/package.test.ts new file mode 100644 index 0000000000000..1b2328788dba6 --- /dev/null +++ b/packages/kbn-packages/src/package.test.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Fs from 'fs'; +import Path from 'path'; + +import { Package } from './package'; + +const OWN_BAZEL_BUILD_FILE = Fs.readFileSync(Path.resolve(__dirname, '../BUILD.bazel'), 'utf8'); + +describe('hasBuildRule()', () => { + it('returns true if there is a rule with the name "build"', () => { + const pkg = new Package('foo', {}, OWN_BAZEL_BUILD_FILE); + expect(pkg.hasBuildRule()).toBe(true); + }); + + it('returns false if there is no rule with name "build"', () => { + const pkg = new Package('foo', {}, ``); + expect(pkg.hasBuildRule()).toBe(false); + }); +}); + +describe('hasBuildTypesRule()', () => { + it('returns true if there is a rule with the name "build_types"', () => { + const pkg = new Package('foo', {}, OWN_BAZEL_BUILD_FILE); + expect(pkg.hasBuildTypesRule()).toBe(true); + }); + + it('returns false if there is no rule with name "build_types"', () => { + const pkg = new Package('foo', {}, ``); + expect(pkg.hasBuildTypesRule()).toBe(false); + }); +}); diff --git a/packages/kbn-packages/src/package.ts b/packages/kbn-packages/src/package.ts new file mode 100644 index 0000000000000..611797dd6b3ec --- /dev/null +++ b/packages/kbn-packages/src/package.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import Fsp from 'fs/promises'; +import { REPO_ROOT } from '@kbn/utils'; + +const BUILD_RULE_NAME = /(^|\s)name\s*=\s*"build"/; +const BUILD_TYPES_RULE_NAME = /(^|\s)name\s*=\s*"build_types"/; + +export class Package { + static async fromDir(dir: string) { + let pkg; + try { + pkg = JSON.parse(await Fsp.readFile(Path.resolve(dir, 'package.json'), 'utf8')); + } catch (error) { + throw new Error(`unable to parse package.json in [${dir}]: ${error.message}`); + } + + let buildBazelContent; + if (pkg.name !== '@kbn/pm') { + try { + buildBazelContent = await Fsp.readFile(Path.resolve(dir, 'BUILD.bazel'), 'utf8'); + } catch (error) { + throw new Error(`unable to read BUILD.bazel file in [${dir}]: ${error.message}`); + } + } + + return new Package(Path.relative(REPO_ROOT, dir), pkg, buildBazelContent); + } + + constructor( + public readonly repoRelativeDir: string, + public readonly pkg: any, + public readonly buildBazelContent?: string + ) {} + + hasBuildRule() { + return !!(this.buildBazelContent && BUILD_RULE_NAME.test(this.buildBazelContent)); + } + + hasBuildTypesRule() { + return !!(this.buildBazelContent && BUILD_TYPES_RULE_NAME.test(this.buildBazelContent)); + } +} diff --git a/packages/kbn-packages/tsconfig.json b/packages/kbn-packages/tsconfig.json new file mode 100644 index 0000000000000..a8cfc2cceb08b --- /dev/null +++ b/packages/kbn-packages/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 62bd4e30250b1..00f13d6695afd 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,7 +94,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(563); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(564); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildBazelProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); @@ -108,7 +108,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(343); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "transformDependencies", function() { return _utils_package_json__WEBPACK_IMPORTED_MODULE_4__["transformDependencies"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(562); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(563); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -141,7 +141,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(129); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(557); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(558); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one @@ -8817,11 +8817,11 @@ exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "commands", function() { return commands; }); /* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(130); -/* harmony import */ var _build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(528); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(529); -/* harmony import */ var _reset__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(553); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(554); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(556); +/* harmony import */ var _build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(529); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(530); +/* harmony import */ var _reset__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(554); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(555); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(557); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -8861,9 +8861,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(340); /* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(408); /* harmony import */ var _utils_sort_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(411); -/* harmony import */ var _utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(419); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(421); -/* harmony import */ var _utils_bazel_setup_remote_cache__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(527); +/* harmony import */ var _utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(420); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(422); +/* harmony import */ var _utils_bazel_setup_remote_cache__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(528); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } @@ -51614,8 +51614,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sortPackageJson", function() { return sortPackageJson; }); /* harmony import */ var fs_promises__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(412); /* harmony import */ var fs_promises__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs_promises__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var sort_package_json__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(413); -/* harmony import */ var sort_package_json__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(sort_package_json__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _kbn_dev_utils_sort_package_json__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(413); +/* harmony import */ var _kbn_dev_utils_sort_package_json__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_sort_package_json__WEBPACK_IMPORTED_MODULE_1__); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -51627,11 +51627,7 @@ __webpack_require__.r(__webpack_exports__); async function sortPackageJson(kbn) { const packageJsonPath = kbn.getAbsolute('package.json'); - const packageJson = await fs_promises__WEBPACK_IMPORTED_MODULE_0___default.a.readFile(packageJsonPath, 'utf-8'); - await fs_promises__WEBPACK_IMPORTED_MODULE_0___default.a.writeFile(packageJsonPath, JSON.stringify(sort_package_json__WEBPACK_IMPORTED_MODULE_1___default()(JSON.parse(packageJson), { - // top level keys in the order they were written when this was implemented - sortOrder: ['name', 'description', 'keywords', 'private', 'version', 'branch', 'types', 'tsdocMetadata', 'build', 'homepage', 'bugs', 'kibana', 'author', 'scripts', 'repository', 'engines', 'resolutions'] - }), null, 2) + '\n'); + await fs_promises__WEBPACK_IMPORTED_MODULE_0___default.a.writeFile(packageJsonPath, Object(_kbn_dev_utils_sort_package_json__WEBPACK_IMPORTED_MODULE_1__["sortPackageJson"])(await fs_promises__WEBPACK_IMPORTED_MODULE_0___default.a.readFile(packageJsonPath, 'utf-8'))); } /***/ }), @@ -51644,11 +51640,41 @@ module.exports = require("fs/promises"); /* 413 */ /***/ (function(module, exports, __webpack_require__) { -const sortObjectKeys = __webpack_require__(414) -const detectIndent = __webpack_require__(415) -const detectNewline = __webpack_require__(416).graceful -const gitHooks = __webpack_require__(417) -const isPlainObject = __webpack_require__(418) +"use strict"; + + +var _interopRequireDefault = __webpack_require__(7); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sortPackageJson = sortPackageJson; + +var _sortPackageJson = _interopRequireDefault(__webpack_require__(414)); + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +function sortPackageJson(json) { + return JSON.stringify((0, _sortPackageJson.default)(JSON.parse(json), { + // top level keys in the order they were written when this was implemented + sortOrder: ['name', 'description', 'keywords', 'private', 'version', 'branch', 'main', 'browser', 'types', 'tsdocMetadata', 'build', 'homepage', 'bugs', 'license', 'kibana', 'author', 'scripts', 'repository', 'engines', 'resolutions'] + }), null, 2) + '\n'; +} + +/***/ }), +/* 414 */ +/***/ (function(module, exports, __webpack_require__) { + +const sortObjectKeys = __webpack_require__(415) +const detectIndent = __webpack_require__(416) +const detectNewline = __webpack_require__(417).graceful +const gitHooks = __webpack_require__(418) +const isPlainObject = __webpack_require__(419) const hasOwnProperty = (object, property) => Object.prototype.hasOwnProperty.call(object, property) @@ -52007,7 +52033,7 @@ module.exports.default = sortPackageJson /***/ }), -/* 414 */ +/* 415 */ /***/ (function(module, exports) { module.exports = function sortObjectByKeyNameList(object, sortWith) { @@ -52031,7 +52057,7 @@ module.exports = function sortObjectByKeyNameList(object, sortWith) { /***/ }), -/* 415 */ +/* 416 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -52198,7 +52224,7 @@ module.exports = string => { /***/ }), -/* 416 */ +/* 417 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -52226,13 +52252,13 @@ module.exports.graceful = string => (typeof string === 'string' && detectNewline /***/ }), -/* 417 */ +/* 418 */ /***/ (function(module) { module.exports = JSON.parse("[\"applypatch-msg\",\"pre-applypatch\",\"post-applypatch\",\"pre-commit\",\"pre-merge-commit\",\"prepare-commit-msg\",\"commit-msg\",\"post-commit\",\"pre-rebase\",\"post-checkout\",\"post-merge\",\"pre-push\",\"pre-receive\",\"update\",\"post-receive\",\"post-update\",\"push-to-checkout\",\"pre-auto-gc\",\"post-rewrite\",\"sendemail-validate\",\"fsmonitor-watchman\",\"p4-pre-submit\",\"post-index-change\"]"); /***/ }), -/* 418 */ +/* 419 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -52249,7 +52275,7 @@ module.exports = value => { /***/ }), -/* 419 */ +/* 420 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52266,7 +52292,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(231); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); /* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(343); -/* harmony import */ var _projects_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(420); +/* harmony import */ var _projects_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(421); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -52447,7 +52473,7 @@ function getDevOnlyProductionDepsTree(kbn, projectName) { } /***/ }), -/* 420 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52589,27 +52615,27 @@ function addProjectToTree(tree, pathParts, project) { } /***/ }), -/* 421 */ +/* 422 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(422); +/* harmony import */ var _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(423); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "yarnIntegrityFileExists", function() { return _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__["yarnIntegrityFileExists"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ensureYarnIntegrityFileExists", function() { return _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__["ensureYarnIntegrityFileExists"]; }); -/* harmony import */ var _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(423); +/* harmony import */ var _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(424); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getBazelDiskCacheFolder", function() { return _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__["getBazelDiskCacheFolder"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getBazelRepositoryCacheFolder", function() { return _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__["getBazelRepositoryCacheFolder"]; }); -/* harmony import */ var _install_tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(424); +/* harmony import */ var _install_tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(425); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isBazelBinAvailable", function() { return _install_tools__WEBPACK_IMPORTED_MODULE_2__["isBazelBinAvailable"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "installBazelTools", function() { return _install_tools__WEBPACK_IMPORTED_MODULE_2__["installBazelTools"]; }); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(425); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(426); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "runBazel", function() { return _run__WEBPACK_IMPORTED_MODULE_3__["runBazel"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "runIBazel", function() { return _run__WEBPACK_IMPORTED_MODULE_3__["runIBazel"]; }); @@ -52627,7 +52653,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 422 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52674,7 +52700,7 @@ async function ensureYarnIntegrityFileExists(nodeModulesPath) { } /***/ }), -/* 423 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52711,7 +52737,7 @@ async function getBazelRepositoryCacheFolder() { } /***/ }), -/* 424 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52830,7 +52856,7 @@ async function installBazelTools(repoRootPath) { } /***/ }), -/* 425 */ +/* 426 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52840,8 +52866,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(114); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(426); -/* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(524); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(427); +/* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(525); /* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(221); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); @@ -52907,141 +52933,141 @@ async function runIBazel(bazelArgs, offline = false, runOpts = {}) { } /***/ }), -/* 426 */ +/* 427 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(427); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(428); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); -/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(428); +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(429); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); -/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(429); +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(430); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); -/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(430); +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(431); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); -/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(431); +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(432); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); -/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(432); +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(433); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); -/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(433); +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(434); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); -/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(434); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(435); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); -/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(435); +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(436); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); -/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(436); +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(437); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); -/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(437); +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(438); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); /* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(81); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); -/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(438); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(439); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); -/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(439); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(440); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); -/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(440); +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(441); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); -/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(441); +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(442); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); -/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(442); +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(443); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); -/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(443); +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(444); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__["defaultIfEmpty"]; }); -/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(444); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(445); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__["delay"]; }); -/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(446); +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(447); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); -/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(447); +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(448); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); -/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(448); +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(449); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); -/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(449); +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(450); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); -/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(450); +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(451); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); -/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(451); +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(452); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); -/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(454); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(455); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); -/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(455); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(456); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); -/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(456); +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(457); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); -/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(457); +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(458); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); -/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(458); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(459); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); /* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(106); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); -/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(459); +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(460); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); -/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(460); +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(461); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); -/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(461); +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(462); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); -/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(462); +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(463); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); /* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(32); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); -/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(463); +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(464); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); -/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(464); +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(465); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); -/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(465); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(466); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "last", function() { return _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__["last"]; }); /* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(67); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); -/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(467); +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(468); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); -/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(468); +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(469); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); -/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(469); +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(470); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); -/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(472); +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(473); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); /* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(82); @@ -53052,175 +53078,175 @@ __webpack_require__.r(__webpack_exports__); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["flatMap"]; }); -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(473); +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(474); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); -/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(474); +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(475); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); -/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(475); +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(476); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); -/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(476); +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(477); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); /* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(42); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); -/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(477); +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(478); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); -/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(478); +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(479); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); -/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(479); +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(480); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); -/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(480); +/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(481); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); -/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(481); +/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(482); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); -/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(482); +/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(483); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); -/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(483); +/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(484); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); -/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(484); +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(485); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); -/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(485); +/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(486); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(470); +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(471); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); -/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(486); +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(487); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(487); +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(488); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); -/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(488); +/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(489); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(489); +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(490); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); /* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(31); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); -/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(490); +/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(491); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); -/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(491); +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(492); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(471); +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(472); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); -/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(492); +/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(493); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(493); +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(494); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(494); +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(495); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); -/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(495); +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(496); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(496); +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(497); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); -/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(497); +/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(498); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(498); +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(499); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(499); +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(500); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); -/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(500); +/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(501); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); -/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(501); +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(502); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); -/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(503); +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(504); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); -/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(504); +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(505); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(505); +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(506); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); -/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(453); +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(454); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(466); +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(467); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); -/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(506); +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(507); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); -/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(507); +/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(508); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); -/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(508); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(509); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(509); +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(510); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); -/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(510); +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(511); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); -/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(452); +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(453); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); -/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(511); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(512); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); -/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(512); +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(513); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); -/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(513); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(514); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); -/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(514); +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(515); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(515); +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(516); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(516); +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(517); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); -/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(517); +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(518); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(518); +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(519); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); -/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(519); +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(520); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(520); +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(521); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(521); +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(522); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__["withLatestFrom"]; }); -/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(522); +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(523); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__["zip"]; }); -/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(523); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(524); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__["zipAll"]; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ @@ -53331,7 +53357,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 427 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53410,14 +53436,14 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 428 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(427); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(428); /* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(109); /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ @@ -53433,7 +53459,7 @@ function auditTime(duration, scheduler) { /***/ }), -/* 429 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53480,7 +53506,7 @@ var BufferSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 430 */ +/* 431 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53581,7 +53607,7 @@ var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 431 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53742,7 +53768,7 @@ function dispatchBufferClose(arg) { /***/ }), -/* 432 */ +/* 433 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53861,7 +53887,7 @@ var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 433 */ +/* 434 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53954,7 +53980,7 @@ var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 434 */ +/* 435 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54014,7 +54040,7 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 435 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54030,7 +54056,7 @@ function combineAll(project) { /***/ }), -/* 436 */ +/* 437 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54062,7 +54088,7 @@ function combineLatest() { /***/ }), -/* 437 */ +/* 438 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54082,7 +54108,7 @@ function concat() { /***/ }), -/* 438 */ +/* 439 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54098,13 +54124,13 @@ function concatMap(project, resultSelector) { /***/ }), -/* 439 */ +/* 440 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); -/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(438); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(439); /** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ function concatMapTo(innerObservable, resultSelector) { @@ -54114,7 +54140,7 @@ function concatMapTo(innerObservable, resultSelector) { /***/ }), -/* 440 */ +/* 441 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54179,7 +54205,7 @@ var CountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 441 */ +/* 442 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54264,7 +54290,7 @@ var DebounceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 442 */ +/* 443 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54340,7 +54366,7 @@ function dispatchNext(subscriber) { /***/ }), -/* 443 */ +/* 444 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54390,7 +54416,7 @@ var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 444 */ +/* 445 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54398,7 +54424,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(445); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(446); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); /* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(43); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -54497,7 +54523,7 @@ var DelayMessage = /*@__PURE__*/ (function () { /***/ }), -/* 445 */ +/* 446 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54511,7 +54537,7 @@ function isDate(value) { /***/ }), -/* 446 */ +/* 447 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54657,7 +54683,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 447 */ +/* 448 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54695,7 +54721,7 @@ var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 448 */ +/* 449 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54771,7 +54797,7 @@ var DistinctSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 449 */ +/* 450 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54842,13 +54868,13 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 450 */ +/* 451 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); -/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(449); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(450); /** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ function distinctUntilKeyChanged(key, compare) { @@ -54858,7 +54884,7 @@ function distinctUntilKeyChanged(key, compare) { /***/ }), -/* 451 */ +/* 452 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54866,9 +54892,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return elementAt; }); /* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(452); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(443); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(453); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(453); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(444); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(454); /** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ @@ -54890,7 +54916,7 @@ function elementAt(index, defaultValue) { /***/ }), -/* 452 */ +/* 453 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54956,7 +54982,7 @@ function defaultErrorFactory() { /***/ }), -/* 453 */ +/* 454 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55018,7 +55044,7 @@ var TakeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 454 */ +/* 455 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55040,7 +55066,7 @@ function endWith() { /***/ }), -/* 455 */ +/* 456 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55102,7 +55128,7 @@ var EverySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 456 */ +/* 457 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55156,7 +55182,7 @@ var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 457 */ +/* 458 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55250,7 +55276,7 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 458 */ +/* 459 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55362,7 +55388,7 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 459 */ +/* 460 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55400,7 +55426,7 @@ var FinallySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 460 */ +/* 461 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55472,13 +55498,13 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 461 */ +/* 462 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); -/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(460); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(461); /** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ function findIndex(predicate, thisArg) { @@ -55488,7 +55514,7 @@ function findIndex(predicate, thisArg) { /***/ }), -/* 462 */ +/* 463 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55496,9 +55522,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "first", function() { return first; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(64); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(453); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(443); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(452); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(454); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(444); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(453); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26); /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -55515,7 +55541,7 @@ function first(predicate, defaultValue) { /***/ }), -/* 463 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55552,7 +55578,7 @@ var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 464 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55596,7 +55622,7 @@ var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 465 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55604,9 +55630,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(64); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(466); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(452); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(443); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(467); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(453); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(444); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26); /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -55623,7 +55649,7 @@ function last(predicate, defaultValue) { /***/ }), -/* 466 */ +/* 467 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55700,7 +55726,7 @@ var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 467 */ +/* 468 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55739,7 +55765,7 @@ var MapToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 468 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55789,13 +55815,13 @@ var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 469 */ +/* 470 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(470); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(471); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function max(comparer) { @@ -55808,15 +55834,15 @@ function max(comparer) { /***/ }), -/* 470 */ +/* 471 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(471); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(466); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(443); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(472); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(467); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(444); /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(25); /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ @@ -55837,7 +55863,7 @@ function reduce(accumulator, seed) { /***/ }), -/* 471 */ +/* 472 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55919,7 +55945,7 @@ var ScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 472 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55939,7 +55965,7 @@ function merge() { /***/ }), -/* 473 */ +/* 474 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55964,7 +55990,7 @@ function mergeMapTo(innerObservable, resultSelector, concurrent) { /***/ }), -/* 474 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56073,13 +56099,13 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 475 */ +/* 476 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(470); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(471); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function min(comparer) { @@ -56092,7 +56118,7 @@ function min(comparer) { /***/ }), -/* 476 */ +/* 477 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56141,7 +56167,7 @@ var MulticastOperator = /*@__PURE__*/ (function () { /***/ }), -/* 477 */ +/* 478 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56231,7 +56257,7 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 478 */ +/* 479 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56279,7 +56305,7 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 479 */ +/* 480 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56302,7 +56328,7 @@ function partition(predicate, thisArg) { /***/ }), -/* 480 */ +/* 481 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56342,14 +56368,14 @@ function plucker(props, length) { /***/ }), -/* 481 */ +/* 482 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return publish; }); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(28); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(476); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(477); /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ @@ -56362,14 +56388,14 @@ function publish(selector) { /***/ }), -/* 482 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return publishBehavior; }); /* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(476); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(477); /** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ @@ -56380,14 +56406,14 @@ function publishBehavior(value) { /***/ }), -/* 483 */ +/* 484 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); /* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(476); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(477); /** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ @@ -56398,14 +56424,14 @@ function publishLast() { /***/ }), -/* 484 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return publishReplay; }); /* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(34); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(476); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(477); /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ @@ -56421,7 +56447,7 @@ function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { /***/ }), -/* 485 */ +/* 486 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56448,7 +56474,7 @@ function race() { /***/ }), -/* 486 */ +/* 487 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56513,7 +56539,7 @@ var RepeatSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 487 */ +/* 488 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56607,7 +56633,7 @@ var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 488 */ +/* 489 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56660,7 +56686,7 @@ var RetrySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 489 */ +/* 490 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56746,7 +56772,7 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 490 */ +/* 491 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56801,7 +56827,7 @@ var SampleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 491 */ +/* 492 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56861,7 +56887,7 @@ function dispatchNotification(state) { /***/ }), -/* 492 */ +/* 493 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56984,13 +57010,13 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 493 */ +/* 494 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "share", function() { return share; }); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(476); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(477); /* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(28); /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ @@ -57007,7 +57033,7 @@ function share() { /***/ }), -/* 494 */ +/* 495 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57076,7 +57102,7 @@ function shareReplayOperator(_a) { /***/ }), -/* 495 */ +/* 496 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57156,7 +57182,7 @@ var SingleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 496 */ +/* 497 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57198,7 +57224,7 @@ var SkipSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 497 */ +/* 498 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57260,7 +57286,7 @@ var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 498 */ +/* 499 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57317,7 +57343,7 @@ var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 499 */ +/* 500 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57373,7 +57399,7 @@ var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 500 */ +/* 501 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57402,13 +57428,13 @@ function startWith() { /***/ }), -/* 501 */ +/* 502 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return subscribeOn; }); -/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(502); +/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(503); /** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ function subscribeOn(scheduler, delay) { @@ -57433,7 +57459,7 @@ var SubscribeOnOperator = /*@__PURE__*/ (function () { /***/ }), -/* 502 */ +/* 503 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57497,13 +57523,13 @@ var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { /***/ }), -/* 503 */ +/* 504 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return switchAll; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(504); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(505); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(26); /** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ @@ -57515,7 +57541,7 @@ function switchAll() { /***/ }), -/* 504 */ +/* 505 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57603,13 +57629,13 @@ var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 505 */ +/* 506 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return switchMapTo; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(504); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(505); /** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ function switchMapTo(innerObservable, resultSelector) { @@ -57619,7 +57645,7 @@ function switchMapTo(innerObservable, resultSelector) { /***/ }), -/* 506 */ +/* 507 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57667,7 +57693,7 @@ var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 507 */ +/* 508 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57735,7 +57761,7 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 508 */ +/* 509 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57823,7 +57849,7 @@ var TapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 509 */ +/* 510 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57925,7 +57951,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 510 */ +/* 511 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57934,7 +57960,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(509); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(510); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ @@ -58023,7 +58049,7 @@ function dispatchNext(arg) { /***/ }), -/* 511 */ +/* 512 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58031,7 +58057,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return timeInterval; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeInterval", function() { return TimeInterval; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(471); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(472); /* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(92); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(67); /** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ @@ -58067,7 +58093,7 @@ var TimeInterval = /*@__PURE__*/ (function () { /***/ }), -/* 512 */ +/* 513 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58075,7 +58101,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return timeout; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); /* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(65); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(513); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(514); /* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(50); /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ @@ -58092,7 +58118,7 @@ function timeout(due, scheduler) { /***/ }), -/* 513 */ +/* 514 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58100,7 +58126,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(445); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(446); /* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ @@ -58171,7 +58197,7 @@ var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 514 */ +/* 515 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58201,13 +58227,13 @@ var Timestamp = /*@__PURE__*/ (function () { /***/ }), -/* 515 */ +/* 516 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return toArray; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(470); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(471); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function toArrayReducer(arr, item, index) { @@ -58224,7 +58250,7 @@ function toArray() { /***/ }), -/* 516 */ +/* 517 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58302,7 +58328,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 517 */ +/* 518 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58392,7 +58418,7 @@ var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 518 */ +/* 519 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58562,7 +58588,7 @@ function dispatchWindowClose(state) { /***/ }), -/* 519 */ +/* 520 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58705,7 +58731,7 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 520 */ +/* 521 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58802,7 +58828,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 521 */ +/* 522 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58897,7 +58923,7 @@ var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 522 */ +/* 523 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58919,7 +58945,7 @@ function zip() { /***/ }), -/* 523 */ +/* 524 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58935,7 +58961,7 @@ function zipAll(project) { /***/ }), -/* 524 */ +/* 525 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58945,7 +58971,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _observe_lines = __webpack_require__(525); +var _observe_lines = __webpack_require__(526); Object.keys(_observe_lines).forEach(function (key) { if (key === "default" || key === "__esModule") return; @@ -58958,7 +58984,7 @@ Object.keys(_observe_lines).forEach(function (key) { }); }); -var _observe_readable = __webpack_require__(526); +var _observe_readable = __webpack_require__(527); Object.keys(_observe_readable).forEach(function (key) { if (key === "default" || key === "__esModule") return; @@ -58972,7 +58998,7 @@ Object.keys(_observe_readable).forEach(function (key) { }); /***/ }), -/* 525 */ +/* 526 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58985,9 +59011,9 @@ exports.observeLines = observeLines; var Rx = _interopRequireWildcard(__webpack_require__(9)); -var _operators = __webpack_require__(426); +var _operators = __webpack_require__(427); -var _observe_readable = __webpack_require__(526); +var _observe_readable = __webpack_require__(527); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } @@ -59050,7 +59076,7 @@ function observeLines(readable) { } /***/ }), -/* 526 */ +/* 527 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59063,7 +59089,7 @@ exports.observeReadable = observeReadable; var Rx = _interopRequireWildcard(__webpack_require__(9)); -var _operators = __webpack_require__(426); +var _operators = __webpack_require__(427); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } @@ -59087,7 +59113,7 @@ function observeReadable(readable) { } /***/ }), -/* 527 */ +/* 528 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59190,13 +59216,13 @@ async function setupRemoteCache(repoRootPath) { } /***/ }), -/* 528 */ +/* 529 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BuildCommand", function() { return BuildCommand; }); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(421); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(422); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -59224,7 +59250,7 @@ const BuildCommand = { }; /***/ }), -/* 529 */ +/* 530 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -59234,11 +59260,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(530); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(531); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(422); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); /* @@ -59341,20 +59367,20 @@ const CleanCommand = { }; /***/ }), -/* 530 */ +/* 531 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readline = __webpack_require__(531); -const chalk = __webpack_require__(532); -const cliCursor = __webpack_require__(535); -const cliSpinners = __webpack_require__(537); -const logSymbols = __webpack_require__(539); -const stripAnsi = __webpack_require__(545); -const wcwidth = __webpack_require__(547); -const isInteractive = __webpack_require__(551); -const MuteStream = __webpack_require__(552); +const readline = __webpack_require__(532); +const chalk = __webpack_require__(533); +const cliCursor = __webpack_require__(536); +const cliSpinners = __webpack_require__(538); +const logSymbols = __webpack_require__(540); +const stripAnsi = __webpack_require__(546); +const wcwidth = __webpack_require__(548); +const isInteractive = __webpack_require__(552); +const MuteStream = __webpack_require__(553); const TEXT = Symbol('text'); const PREFIX_TEXT = Symbol('prefixText'); @@ -59707,13 +59733,13 @@ module.exports.promise = (action, options) => { /***/ }), -/* 531 */ +/* 532 */ /***/ (function(module, exports) { module.exports = require("readline"); /***/ }), -/* 532 */ +/* 533 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59723,7 +59749,7 @@ const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(121); const { stringReplaceAll, stringEncaseCRLFWithFirstIndex -} = __webpack_require__(533); +} = __webpack_require__(534); // `supportsColor.level` → `ansiStyles.color[name]` mapping const levelMapping = [ @@ -59924,7 +59950,7 @@ const chalkTag = (chalk, ...strings) => { } if (template === undefined) { - template = __webpack_require__(534); + template = __webpack_require__(535); } return template(chalk, parts.join('')); @@ -59953,7 +59979,7 @@ module.exports = chalk; /***/ }), -/* 533 */ +/* 534 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59999,7 +60025,7 @@ module.exports = { /***/ }), -/* 534 */ +/* 535 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60140,12 +60166,12 @@ module.exports = (chalk, temporary) => { /***/ }), -/* 535 */ +/* 536 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const restoreCursor = __webpack_require__(536); +const restoreCursor = __webpack_require__(537); let isHidden = false; @@ -60182,7 +60208,7 @@ exports.toggle = (force, writableStream) => { /***/ }), -/* 536 */ +/* 537 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60198,13 +60224,13 @@ module.exports = onetime(() => { /***/ }), -/* 537 */ +/* 538 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const spinners = Object.assign({}, __webpack_require__(538)); +const spinners = Object.assign({}, __webpack_require__(539)); const spinnersList = Object.keys(spinners); @@ -60222,18 +60248,18 @@ module.exports.default = spinners; /***/ }), -/* 538 */ +/* 539 */ /***/ (function(module) { module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"dots8Bit\":{\"interval\":80,\"frames\":[\"⠀\",\"⠁\",\"⠂\",\"⠃\",\"⠄\",\"⠅\",\"⠆\",\"⠇\",\"⡀\",\"⡁\",\"⡂\",\"⡃\",\"⡄\",\"⡅\",\"⡆\",\"⡇\",\"⠈\",\"⠉\",\"⠊\",\"⠋\",\"⠌\",\"⠍\",\"⠎\",\"⠏\",\"⡈\",\"⡉\",\"⡊\",\"⡋\",\"⡌\",\"⡍\",\"⡎\",\"⡏\",\"⠐\",\"⠑\",\"⠒\",\"⠓\",\"⠔\",\"⠕\",\"⠖\",\"⠗\",\"⡐\",\"⡑\",\"⡒\",\"⡓\",\"⡔\",\"⡕\",\"⡖\",\"⡗\",\"⠘\",\"⠙\",\"⠚\",\"⠛\",\"⠜\",\"⠝\",\"⠞\",\"⠟\",\"⡘\",\"⡙\",\"⡚\",\"⡛\",\"⡜\",\"⡝\",\"⡞\",\"⡟\",\"⠠\",\"⠡\",\"⠢\",\"⠣\",\"⠤\",\"⠥\",\"⠦\",\"⠧\",\"⡠\",\"⡡\",\"⡢\",\"⡣\",\"⡤\",\"⡥\",\"⡦\",\"⡧\",\"⠨\",\"⠩\",\"⠪\",\"⠫\",\"⠬\",\"⠭\",\"⠮\",\"⠯\",\"⡨\",\"⡩\",\"⡪\",\"⡫\",\"⡬\",\"⡭\",\"⡮\",\"⡯\",\"⠰\",\"⠱\",\"⠲\",\"⠳\",\"⠴\",\"⠵\",\"⠶\",\"⠷\",\"⡰\",\"⡱\",\"⡲\",\"⡳\",\"⡴\",\"⡵\",\"⡶\",\"⡷\",\"⠸\",\"⠹\",\"⠺\",\"⠻\",\"⠼\",\"⠽\",\"⠾\",\"⠿\",\"⡸\",\"⡹\",\"⡺\",\"⡻\",\"⡼\",\"⡽\",\"⡾\",\"⡿\",\"⢀\",\"⢁\",\"⢂\",\"⢃\",\"⢄\",\"⢅\",\"⢆\",\"⢇\",\"⣀\",\"⣁\",\"⣂\",\"⣃\",\"⣄\",\"⣅\",\"⣆\",\"⣇\",\"⢈\",\"⢉\",\"⢊\",\"⢋\",\"⢌\",\"⢍\",\"⢎\",\"⢏\",\"⣈\",\"⣉\",\"⣊\",\"⣋\",\"⣌\",\"⣍\",\"⣎\",\"⣏\",\"⢐\",\"⢑\",\"⢒\",\"⢓\",\"⢔\",\"⢕\",\"⢖\",\"⢗\",\"⣐\",\"⣑\",\"⣒\",\"⣓\",\"⣔\",\"⣕\",\"⣖\",\"⣗\",\"⢘\",\"⢙\",\"⢚\",\"⢛\",\"⢜\",\"⢝\",\"⢞\",\"⢟\",\"⣘\",\"⣙\",\"⣚\",\"⣛\",\"⣜\",\"⣝\",\"⣞\",\"⣟\",\"⢠\",\"⢡\",\"⢢\",\"⢣\",\"⢤\",\"⢥\",\"⢦\",\"⢧\",\"⣠\",\"⣡\",\"⣢\",\"⣣\",\"⣤\",\"⣥\",\"⣦\",\"⣧\",\"⢨\",\"⢩\",\"⢪\",\"⢫\",\"⢬\",\"⢭\",\"⢮\",\"⢯\",\"⣨\",\"⣩\",\"⣪\",\"⣫\",\"⣬\",\"⣭\",\"⣮\",\"⣯\",\"⢰\",\"⢱\",\"⢲\",\"⢳\",\"⢴\",\"⢵\",\"⢶\",\"⢷\",\"⣰\",\"⣱\",\"⣲\",\"⣳\",\"⣴\",\"⣵\",\"⣶\",\"⣷\",\"⢸\",\"⢹\",\"⢺\",\"⢻\",\"⢼\",\"⢽\",\"⢾\",\"⢿\",\"⣸\",\"⣹\",\"⣺\",\"⣻\",\"⣼\",\"⣽\",\"⣾\",\"⣿\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕛 \",\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"material\":{\"interval\":17,\"frames\":[\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███████▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"██████████▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"█████████████▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁██████████████▁▁▁▁\",\"▁▁▁██████████████▁▁▁\",\"▁▁▁▁█████████████▁▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁▁█████████████▁▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁▁███████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁▁█████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]},\"grenade\":{\"interval\":80,\"frames\":[\"، \",\"′ \",\" ´ \",\" ‾ \",\" ⸌\",\" ⸊\",\" |\",\" ⁎\",\" ⁕\",\" ෴ \",\" ⁓\",\" \",\" \",\" \"]},\"point\":{\"interval\":125,\"frames\":[\"∙∙∙\",\"●∙∙\",\"∙●∙\",\"∙∙●\",\"∙∙∙\"]},\"layer\":{\"interval\":150,\"frames\":[\"-\",\"=\",\"≡\"]},\"betaWave\":{\"interval\":80,\"frames\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]},\"aesthetic\":{\"interval\":80,\"frames\":[\"▰▱▱▱▱▱▱\",\"▰▰▱▱▱▱▱\",\"▰▰▰▱▱▱▱\",\"▰▰▰▰▱▱▱\",\"▰▰▰▰▰▱▱\",\"▰▰▰▰▰▰▱\",\"▰▰▰▰▰▰▰\",\"▰▱▱▱▱▱▱\"]}}"); /***/ }), -/* 539 */ +/* 540 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(540); +const chalk = __webpack_require__(541); const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; @@ -60255,16 +60281,16 @@ module.exports = isSupported ? main : fallbacks; /***/ }), -/* 540 */ +/* 541 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(357); -const ansiStyles = __webpack_require__(541); -const stdoutColor = __webpack_require__(542).stdout; +const ansiStyles = __webpack_require__(542); +const stdoutColor = __webpack_require__(543).stdout; -const template = __webpack_require__(544); +const template = __webpack_require__(545); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -60490,7 +60516,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 541 */ +/* 542 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60663,13 +60689,13 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) /***/ }), -/* 542 */ +/* 543 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const os = __webpack_require__(122); -const hasFlag = __webpack_require__(543); +const hasFlag = __webpack_require__(544); const env = process.env; @@ -60801,7 +60827,7 @@ module.exports = { /***/ }), -/* 543 */ +/* 544 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60816,7 +60842,7 @@ module.exports = (flag, argv) => { /***/ }), -/* 544 */ +/* 545 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60951,18 +60977,18 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 545 */ +/* 546 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(546); +const ansiRegex = __webpack_require__(547); module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; /***/ }), -/* 546 */ +/* 547 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60979,14 +61005,14 @@ module.exports = ({onlyFirst = false} = {}) => { /***/ }), -/* 547 */ +/* 548 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var defaults = __webpack_require__(548) -var combining = __webpack_require__(550) +var defaults = __webpack_require__(549) +var combining = __webpack_require__(551) var DEFAULTS = { nul: 0, @@ -61085,10 +61111,10 @@ function bisearch(ucs) { /***/ }), -/* 548 */ +/* 549 */ /***/ (function(module, exports, __webpack_require__) { -var clone = __webpack_require__(549); +var clone = __webpack_require__(550); module.exports = function(options, defaults) { options = options || {}; @@ -61103,7 +61129,7 @@ module.exports = function(options, defaults) { }; /***/ }), -/* 549 */ +/* 550 */ /***/ (function(module, exports, __webpack_require__) { var clone = (function() { @@ -61275,7 +61301,7 @@ if ( true && module.exports) { /***/ }), -/* 550 */ +/* 551 */ /***/ (function(module, exports) { module.exports = [ @@ -61331,7 +61357,7 @@ module.exports = [ /***/ }), -/* 551 */ +/* 552 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61347,7 +61373,7 @@ module.exports = ({stream = process.stdout} = {}) => { /***/ }), -/* 552 */ +/* 553 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(173) @@ -61498,7 +61524,7 @@ MuteStream.prototype.close = proxy('close') /***/ }), -/* 553 */ +/* 554 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -61508,11 +61534,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(530); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(531); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(422); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); /* @@ -61621,7 +61647,7 @@ const ResetCommand = { }; /***/ }), -/* 554 */ +/* 555 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -61631,7 +61657,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(341); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); -/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(555); +/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(556); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(340); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one @@ -61690,7 +61716,7 @@ const RunCommand = { }; /***/ }), -/* 555 */ +/* 556 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -61745,13 +61771,13 @@ async function parallelize(items, fn, concurrency = 4) { } /***/ }), -/* 556 */ +/* 557 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WatchCommand", function() { return WatchCommand; }); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(421); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(422); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -61782,7 +61808,7 @@ const WatchCommand = { }; /***/ }), -/* 557 */ +/* 558 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -61793,8 +61819,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(341); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(340); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(420); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(558); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(559); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } @@ -61913,7 +61939,7 @@ function toArray(value) { } /***/ }), -/* 558 */ +/* 559 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -61923,13 +61949,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(132); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(559); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(560); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(333); /* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(408); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(340); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(562); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(563); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } @@ -62093,15 +62119,15 @@ class Kibana { } /***/ }), -/* 559 */ +/* 560 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const minimatch = __webpack_require__(247); const arrayUnion = __webpack_require__(242); -const arrayDiffer = __webpack_require__(560); -const arrify = __webpack_require__(561); +const arrayDiffer = __webpack_require__(561); +const arrify = __webpack_require__(562); module.exports = (list, patterns, options = {}) => { list = arrify(list); @@ -62125,7 +62151,7 @@ module.exports = (list, patterns, options = {}) => { /***/ }), -/* 560 */ +/* 561 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62140,7 +62166,7 @@ module.exports = arrayDiffer; /***/ }), -/* 561 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62170,7 +62196,7 @@ module.exports = arrify; /***/ }), -/* 562 */ +/* 563 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -62230,15 +62256,15 @@ function getProjectPaths({ } /***/ }), -/* 563 */ +/* 564 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(564); +/* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(565); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildBazelProductionProjects"]; }); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(805); +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(806); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); /* @@ -62252,20 +62278,20 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 564 */ +/* 565 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return buildBazelProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(565); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(566); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(777); +/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(778); /* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(globby__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(805); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(806); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(422); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(343); @@ -62359,7 +62385,7 @@ async function applyCorrectPermissions(project, kibanaRoot, buildRoot) { } /***/ }), -/* 565 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62367,14 +62393,14 @@ async function applyCorrectPermissions(project, kibanaRoot, buildRoot) { const EventEmitter = __webpack_require__(164); const path = __webpack_require__(4); const os = __webpack_require__(122); -const pMap = __webpack_require__(566); -const arrify = __webpack_require__(561); -const globby = __webpack_require__(569); -const hasGlob = __webpack_require__(761); -const cpFile = __webpack_require__(763); -const junk = __webpack_require__(773); -const pFilter = __webpack_require__(774); -const CpyError = __webpack_require__(776); +const pMap = __webpack_require__(567); +const arrify = __webpack_require__(562); +const globby = __webpack_require__(570); +const hasGlob = __webpack_require__(762); +const cpFile = __webpack_require__(764); +const junk = __webpack_require__(774); +const pFilter = __webpack_require__(775); +const CpyError = __webpack_require__(777); const defaultOptions = { ignoreJunk: true @@ -62525,12 +62551,12 @@ module.exports = (source, destination, { /***/ }), -/* 566 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(567); +const AggregateError = __webpack_require__(568); module.exports = async ( iterable, @@ -62613,12 +62639,12 @@ module.exports = async ( /***/ }), -/* 567 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(568); +const indentString = __webpack_require__(569); const cleanStack = __webpack_require__(338); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -62667,7 +62693,7 @@ module.exports = AggregateError; /***/ }), -/* 568 */ +/* 569 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62709,17 +62735,17 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 569 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const arrayUnion = __webpack_require__(570); +const arrayUnion = __webpack_require__(571); const glob = __webpack_require__(244); -const fastGlob = __webpack_require__(572); -const dirGlob = __webpack_require__(755); -const gitignore = __webpack_require__(758); +const fastGlob = __webpack_require__(573); +const dirGlob = __webpack_require__(756); +const gitignore = __webpack_require__(759); const DEFAULT_FILTER = () => false; @@ -62864,12 +62890,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 570 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(571); +var arrayUniq = __webpack_require__(572); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -62877,7 +62903,7 @@ module.exports = function () { /***/ }), -/* 571 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62946,10 +62972,10 @@ if ('Set' in global) { /***/ }), -/* 572 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(573); +const pkg = __webpack_require__(574); module.exports = pkg.async; module.exports.default = pkg.async; @@ -62962,19 +62988,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 573 */ +/* 574 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(574); -var taskManager = __webpack_require__(575); -var reader_async_1 = __webpack_require__(726); -var reader_stream_1 = __webpack_require__(750); -var reader_sync_1 = __webpack_require__(751); -var arrayUtils = __webpack_require__(753); -var streamUtils = __webpack_require__(754); +var optionsManager = __webpack_require__(575); +var taskManager = __webpack_require__(576); +var reader_async_1 = __webpack_require__(727); +var reader_stream_1 = __webpack_require__(751); +var reader_sync_1 = __webpack_require__(752); +var arrayUtils = __webpack_require__(754); +var streamUtils = __webpack_require__(755); /** * Synchronous API. */ @@ -63040,7 +63066,7 @@ function isString(source) { /***/ }), -/* 574 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63078,13 +63104,13 @@ exports.prepare = prepare; /***/ }), -/* 575 */ +/* 576 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(576); +var patternUtils = __webpack_require__(577); /** * Generate tasks based on parent directory of each pattern. */ @@ -63175,16 +63201,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 576 */ +/* 577 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var globParent = __webpack_require__(577); +var globParent = __webpack_require__(578); var isGlob = __webpack_require__(266); -var micromatch = __webpack_require__(580); +var micromatch = __webpack_require__(581); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -63330,15 +63356,15 @@ exports.matchAny = matchAny; /***/ }), -/* 577 */ +/* 578 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(4); -var isglob = __webpack_require__(578); -var pathDirname = __webpack_require__(579); +var isglob = __webpack_require__(579); +var pathDirname = __webpack_require__(580); var isWin32 = __webpack_require__(122).platform() === 'win32'; module.exports = function globParent(str) { @@ -63361,7 +63387,7 @@ module.exports = function globParent(str) { /***/ }), -/* 578 */ +/* 579 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -63392,7 +63418,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 579 */ +/* 580 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63542,7 +63568,7 @@ module.exports.win32 = win32; /***/ }), -/* 580 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63553,18 +63579,18 @@ module.exports.win32 = win32; */ var util = __webpack_require__(113); -var braces = __webpack_require__(581); -var toRegex = __webpack_require__(582); -var extend = __webpack_require__(694); +var braces = __webpack_require__(582); +var toRegex = __webpack_require__(583); +var extend = __webpack_require__(695); /** * Local dependencies */ -var compilers = __webpack_require__(696); -var parsers = __webpack_require__(722); -var cache = __webpack_require__(723); -var utils = __webpack_require__(724); +var compilers = __webpack_require__(697); +var parsers = __webpack_require__(723); +var cache = __webpack_require__(724); +var utils = __webpack_require__(725); var MAX_LENGTH = 1024 * 64; /** @@ -64426,7 +64452,7 @@ module.exports = micromatch; /***/ }), -/* 581 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64436,18 +64462,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(582); -var unique = __webpack_require__(602); -var extend = __webpack_require__(603); +var toRegex = __webpack_require__(583); +var unique = __webpack_require__(603); +var extend = __webpack_require__(604); /** * Local dependencies */ -var compilers = __webpack_require__(605); -var parsers = __webpack_require__(620); -var Braces = __webpack_require__(625); -var utils = __webpack_require__(606); +var compilers = __webpack_require__(606); +var parsers = __webpack_require__(621); +var Braces = __webpack_require__(626); +var utils = __webpack_require__(607); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -64751,16 +64777,16 @@ module.exports = braces; /***/ }), -/* 582 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(583); -var define = __webpack_require__(589); -var extend = __webpack_require__(595); -var not = __webpack_require__(599); +var safe = __webpack_require__(584); +var define = __webpack_require__(590); +var extend = __webpack_require__(596); +var not = __webpack_require__(600); var MAX_LENGTH = 1024 * 64; /** @@ -64913,10 +64939,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 583 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(584); +var parse = __webpack_require__(585); var types = parse.types; module.exports = function (re, opts) { @@ -64962,13 +64988,13 @@ function isRegExp (x) { /***/ }), -/* 584 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(585); -var types = __webpack_require__(586); -var sets = __webpack_require__(587); -var positions = __webpack_require__(588); +var util = __webpack_require__(586); +var types = __webpack_require__(587); +var sets = __webpack_require__(588); +var positions = __webpack_require__(589); module.exports = function(regexpStr) { @@ -65250,11 +65276,11 @@ module.exports.types = types; /***/ }), -/* 585 */ +/* 586 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(586); -var sets = __webpack_require__(587); +var types = __webpack_require__(587); +var sets = __webpack_require__(588); // All of these are private and only used by randexp. @@ -65367,7 +65393,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 586 */ +/* 587 */ /***/ (function(module, exports) { module.exports = { @@ -65383,10 +65409,10 @@ module.exports = { /***/ }), -/* 587 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(586); +var types = __webpack_require__(587); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -65471,10 +65497,10 @@ exports.anyChar = function() { /***/ }), -/* 588 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(586); +var types = __webpack_require__(587); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -65494,7 +65520,7 @@ exports.end = function() { /***/ }), -/* 589 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65507,8 +65533,8 @@ exports.end = function() { -var isobject = __webpack_require__(590); -var isDescriptor = __webpack_require__(591); +var isobject = __webpack_require__(591); +var isDescriptor = __webpack_require__(592); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -65539,7 +65565,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 590 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65558,7 +65584,7 @@ module.exports = function isObject(val) { /***/ }), -/* 591 */ +/* 592 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65571,9 +65597,9 @@ module.exports = function isObject(val) { -var typeOf = __webpack_require__(592); -var isAccessor = __webpack_require__(593); -var isData = __webpack_require__(594); +var typeOf = __webpack_require__(593); +var isAccessor = __webpack_require__(594); +var isData = __webpack_require__(595); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -65587,7 +65613,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 592 */ +/* 593 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -65722,7 +65748,7 @@ function isBuffer(val) { /***/ }), -/* 593 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65735,7 +65761,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(592); +var typeOf = __webpack_require__(593); // accessor descriptor properties var accessor = { @@ -65798,7 +65824,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 594 */ +/* 595 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65811,7 +65837,7 @@ module.exports = isAccessorDescriptor; -var typeOf = __webpack_require__(592); +var typeOf = __webpack_require__(593); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -65854,14 +65880,14 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 595 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(596); -var assignSymbols = __webpack_require__(598); +var isExtendable = __webpack_require__(597); +var assignSymbols = __webpack_require__(599); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -65921,7 +65947,7 @@ function isEnum(obj, key) { /***/ }), -/* 596 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65934,7 +65960,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(597); +var isPlainObject = __webpack_require__(598); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -65942,7 +65968,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 597 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65955,7 +65981,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(590); +var isObject = __webpack_require__(591); function isObjectObject(o) { return isObject(o) === true @@ -65986,7 +66012,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 598 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66033,14 +66059,14 @@ module.exports = function(receiver, objects) { /***/ }), -/* 599 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(600); -var safe = __webpack_require__(583); +var extend = __webpack_require__(601); +var safe = __webpack_require__(584); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -66112,14 +66138,14 @@ module.exports = toRegex; /***/ }), -/* 600 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(601); -var assignSymbols = __webpack_require__(598); +var isExtendable = __webpack_require__(602); +var assignSymbols = __webpack_require__(599); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -66179,7 +66205,7 @@ function isEnum(obj, key) { /***/ }), -/* 601 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66192,7 +66218,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(597); +var isPlainObject = __webpack_require__(598); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -66200,7 +66226,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 602 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66250,13 +66276,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 603 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(604); +var isObject = __webpack_require__(605); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -66290,7 +66316,7 @@ function hasOwn(obj, key) { /***/ }), -/* 604 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66310,13 +66336,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 605 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(606); +var utils = __webpack_require__(607); module.exports = function(braces, options) { braces.compiler @@ -66599,25 +66625,25 @@ function hasQueue(node) { /***/ }), -/* 606 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(607); +var splitString = __webpack_require__(608); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(603); -utils.flatten = __webpack_require__(610); -utils.isObject = __webpack_require__(590); -utils.fillRange = __webpack_require__(611); -utils.repeat = __webpack_require__(619); -utils.unique = __webpack_require__(602); +utils.extend = __webpack_require__(604); +utils.flatten = __webpack_require__(611); +utils.isObject = __webpack_require__(591); +utils.fillRange = __webpack_require__(612); +utils.repeat = __webpack_require__(620); +utils.unique = __webpack_require__(603); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -66949,7 +66975,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 607 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66962,7 +66988,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(608); +var extend = __webpack_require__(609); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -67127,14 +67153,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 608 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(609); -var assignSymbols = __webpack_require__(598); +var isExtendable = __webpack_require__(610); +var assignSymbols = __webpack_require__(599); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -67194,7 +67220,7 @@ function isEnum(obj, key) { /***/ }), -/* 609 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67207,7 +67233,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(597); +var isPlainObject = __webpack_require__(598); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -67215,7 +67241,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 610 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67244,7 +67270,7 @@ function flat(arr, res) { /***/ }), -/* 611 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67258,10 +67284,10 @@ function flat(arr, res) { var util = __webpack_require__(113); -var isNumber = __webpack_require__(612); -var extend = __webpack_require__(615); -var repeat = __webpack_require__(617); -var toRegex = __webpack_require__(618); +var isNumber = __webpack_require__(613); +var extend = __webpack_require__(616); +var repeat = __webpack_require__(618); +var toRegex = __webpack_require__(619); /** * Return a range of numbers or letters. @@ -67459,7 +67485,7 @@ module.exports = fillRange; /***/ }), -/* 612 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67472,7 +67498,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(613); +var typeOf = __webpack_require__(614); module.exports = function isNumber(num) { var type = typeOf(num); @@ -67488,10 +67514,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 613 */ +/* 614 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(614); +var isBuffer = __webpack_require__(615); var toString = Object.prototype.toString; /** @@ -67610,7 +67636,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 614 */ +/* 615 */ /***/ (function(module, exports) { /*! @@ -67637,13 +67663,13 @@ function isSlowBuffer (obj) { /***/ }), -/* 615 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(616); +var isObject = __webpack_require__(617); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -67677,7 +67703,7 @@ function hasOwn(obj, key) { /***/ }), -/* 616 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67697,7 +67723,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 617 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67774,7 +67800,7 @@ function repeat(str, num) { /***/ }), -/* 618 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67787,8 +67813,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(617); -var isNumber = __webpack_require__(612); +var repeat = __webpack_require__(618); +var isNumber = __webpack_require__(613); var cache = {}; function toRegexRange(min, max, options) { @@ -68075,7 +68101,7 @@ module.exports = toRegexRange; /***/ }), -/* 619 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68100,14 +68126,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 620 */ +/* 621 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(621); -var utils = __webpack_require__(606); +var Node = __webpack_require__(622); +var utils = __webpack_require__(607); /** * Braces parsers @@ -68467,15 +68493,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 621 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(590); -var define = __webpack_require__(622); -var utils = __webpack_require__(623); +var isObject = __webpack_require__(591); +var define = __webpack_require__(623); +var utils = __webpack_require__(624); var ownNames; /** @@ -68966,7 +68992,7 @@ exports = module.exports = Node; /***/ }), -/* 622 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68979,7 +69005,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(591); +var isDescriptor = __webpack_require__(592); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -69004,13 +69030,13 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 623 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(624); +var typeOf = __webpack_require__(625); var utils = module.exports; /** @@ -70030,10 +70056,10 @@ function assert(val, message) { /***/ }), -/* 624 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(614); +var isBuffer = __webpack_require__(615); var toString = Object.prototype.toString; /** @@ -70152,17 +70178,17 @@ module.exports = function kindOf(val) { /***/ }), -/* 625 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(603); -var Snapdragon = __webpack_require__(626); -var compilers = __webpack_require__(605); -var parsers = __webpack_require__(620); -var utils = __webpack_require__(606); +var extend = __webpack_require__(604); +var Snapdragon = __webpack_require__(627); +var compilers = __webpack_require__(606); +var parsers = __webpack_require__(621); +var utils = __webpack_require__(607); /** * Customize Snapdragon parser and renderer @@ -70263,17 +70289,17 @@ module.exports = Braces; /***/ }), -/* 626 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(627); -var define = __webpack_require__(657); -var Compiler = __webpack_require__(668); -var Parser = __webpack_require__(691); -var utils = __webpack_require__(671); +var Base = __webpack_require__(628); +var define = __webpack_require__(658); +var Compiler = __webpack_require__(669); +var Parser = __webpack_require__(692); +var utils = __webpack_require__(672); var regexCache = {}; var cache = {}; @@ -70444,20 +70470,20 @@ module.exports.Parser = Parser; /***/ }), -/* 627 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(113); -var define = __webpack_require__(628); -var CacheBase = __webpack_require__(629); -var Emitter = __webpack_require__(630); -var isObject = __webpack_require__(590); -var merge = __webpack_require__(651); -var pascal = __webpack_require__(654); -var cu = __webpack_require__(655); +var define = __webpack_require__(629); +var CacheBase = __webpack_require__(630); +var Emitter = __webpack_require__(631); +var isObject = __webpack_require__(591); +var merge = __webpack_require__(652); +var pascal = __webpack_require__(655); +var cu = __webpack_require__(656); /** * Optionally define a custom `cache` namespace to use. @@ -70886,7 +70912,7 @@ module.exports.namespace = namespace; /***/ }), -/* 628 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70899,7 +70925,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(591); +var isDescriptor = __webpack_require__(592); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -70924,21 +70950,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 629 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(590); -var Emitter = __webpack_require__(630); -var visit = __webpack_require__(631); -var toPath = __webpack_require__(634); -var union = __webpack_require__(636); -var del = __webpack_require__(642); -var get = __webpack_require__(639); -var has = __webpack_require__(647); -var set = __webpack_require__(650); +var isObject = __webpack_require__(591); +var Emitter = __webpack_require__(631); +var visit = __webpack_require__(632); +var toPath = __webpack_require__(635); +var union = __webpack_require__(637); +var del = __webpack_require__(643); +var get = __webpack_require__(640); +var has = __webpack_require__(648); +var set = __webpack_require__(651); /** * Create a `Cache` constructor that when instantiated will @@ -71192,7 +71218,7 @@ module.exports.namespace = namespace; /***/ }), -/* 630 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { @@ -71361,7 +71387,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 631 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71374,8 +71400,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(632); -var mapVisit = __webpack_require__(633); +var visit = __webpack_require__(633); +var mapVisit = __webpack_require__(634); module.exports = function(collection, method, val) { var result; @@ -71398,7 +71424,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 632 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71411,7 +71437,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(590); +var isObject = __webpack_require__(591); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -71438,14 +71464,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 633 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(113); -var visit = __webpack_require__(632); +var visit = __webpack_require__(633); /** * Map `visit` over an array of objects. @@ -71482,7 +71508,7 @@ function isObject(val) { /***/ }), -/* 634 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71495,7 +71521,7 @@ function isObject(val) { -var typeOf = __webpack_require__(635); +var typeOf = __webpack_require__(636); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -71522,10 +71548,10 @@ function filter(arr) { /***/ }), -/* 635 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(614); +var isBuffer = __webpack_require__(615); var toString = Object.prototype.toString; /** @@ -71644,16 +71670,16 @@ module.exports = function kindOf(val) { /***/ }), -/* 636 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(637); -var union = __webpack_require__(638); -var get = __webpack_require__(639); -var set = __webpack_require__(640); +var isObject = __webpack_require__(638); +var union = __webpack_require__(639); +var get = __webpack_require__(640); +var set = __webpack_require__(641); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -71681,7 +71707,7 @@ function arrayify(val) { /***/ }), -/* 637 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71701,7 +71727,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 638 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71737,7 +71763,7 @@ module.exports = function union(init) { /***/ }), -/* 639 */ +/* 640 */ /***/ (function(module, exports) { /*! @@ -71793,7 +71819,7 @@ function toString(val) { /***/ }), -/* 640 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71806,10 +71832,10 @@ function toString(val) { -var split = __webpack_require__(607); -var extend = __webpack_require__(641); -var isPlainObject = __webpack_require__(597); -var isObject = __webpack_require__(637); +var split = __webpack_require__(608); +var extend = __webpack_require__(642); +var isPlainObject = __webpack_require__(598); +var isObject = __webpack_require__(638); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -71855,13 +71881,13 @@ function isValidKey(key) { /***/ }), -/* 641 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(637); +var isObject = __webpack_require__(638); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -71895,7 +71921,7 @@ function hasOwn(obj, key) { /***/ }), -/* 642 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71908,8 +71934,8 @@ function hasOwn(obj, key) { -var isObject = __webpack_require__(590); -var has = __webpack_require__(643); +var isObject = __webpack_require__(591); +var has = __webpack_require__(644); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -71934,7 +71960,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 643 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71947,9 +71973,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(644); -var hasValues = __webpack_require__(646); -var get = __webpack_require__(639); +var isObject = __webpack_require__(645); +var hasValues = __webpack_require__(647); +var get = __webpack_require__(640); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -71960,7 +71986,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 644 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71973,7 +71999,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(645); +var isArray = __webpack_require__(646); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -71981,7 +72007,7 @@ module.exports = function isObject(val) { /***/ }), -/* 645 */ +/* 646 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -71992,7 +72018,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 646 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72035,7 +72061,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 647 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72048,9 +72074,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(590); -var hasValues = __webpack_require__(648); -var get = __webpack_require__(639); +var isObject = __webpack_require__(591); +var hasValues = __webpack_require__(649); +var get = __webpack_require__(640); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -72058,7 +72084,7 @@ module.exports = function(val, prop) { /***/ }), -/* 648 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72071,8 +72097,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(649); -var isNumber = __webpack_require__(612); +var typeOf = __webpack_require__(650); +var isNumber = __webpack_require__(613); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -72125,10 +72151,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 649 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(614); +var isBuffer = __webpack_require__(615); var toString = Object.prototype.toString; /** @@ -72250,7 +72276,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 650 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72263,10 +72289,10 @@ module.exports = function kindOf(val) { -var split = __webpack_require__(607); -var extend = __webpack_require__(641); -var isPlainObject = __webpack_require__(597); -var isObject = __webpack_require__(637); +var split = __webpack_require__(608); +var extend = __webpack_require__(642); +var isPlainObject = __webpack_require__(598); +var isObject = __webpack_require__(638); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -72312,14 +72338,14 @@ function isValidKey(key) { /***/ }), -/* 651 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(652); -var forIn = __webpack_require__(653); +var isExtendable = __webpack_require__(653); +var forIn = __webpack_require__(654); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -72383,7 +72409,7 @@ module.exports = mixinDeep; /***/ }), -/* 652 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72396,7 +72422,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(597); +var isPlainObject = __webpack_require__(598); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -72404,7 +72430,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 653 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72427,7 +72453,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 654 */ +/* 655 */ /***/ (function(module, exports) { /*! @@ -72454,14 +72480,14 @@ module.exports = pascalcase; /***/ }), -/* 655 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(113); -var utils = __webpack_require__(656); +var utils = __webpack_require__(657); /** * Expose class utils @@ -72826,7 +72852,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 656 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72840,10 +72866,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(638); -utils.define = __webpack_require__(657); -utils.isObj = __webpack_require__(590); -utils.staticExtend = __webpack_require__(664); +utils.union = __webpack_require__(639); +utils.define = __webpack_require__(658); +utils.isObj = __webpack_require__(591); +utils.staticExtend = __webpack_require__(665); /** @@ -72854,7 +72880,7 @@ module.exports = utils; /***/ }), -/* 657 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72867,7 +72893,7 @@ module.exports = utils; -var isDescriptor = __webpack_require__(658); +var isDescriptor = __webpack_require__(659); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -72892,7 +72918,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 658 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72905,9 +72931,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(659); -var isAccessor = __webpack_require__(660); -var isData = __webpack_require__(662); +var typeOf = __webpack_require__(660); +var isAccessor = __webpack_require__(661); +var isData = __webpack_require__(663); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -72921,7 +72947,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 659 */ +/* 660 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -73074,7 +73100,7 @@ function isBuffer(val) { /***/ }), -/* 660 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73087,7 +73113,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(661); +var typeOf = __webpack_require__(662); // accessor descriptor properties var accessor = { @@ -73150,10 +73176,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 661 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(614); +var isBuffer = __webpack_require__(615); var toString = Object.prototype.toString; /** @@ -73272,7 +73298,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 662 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73285,7 +73311,7 @@ module.exports = function kindOf(val) { -var typeOf = __webpack_require__(663); +var typeOf = __webpack_require__(664); // data descriptor properties var data = { @@ -73334,10 +73360,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 663 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(614); +var isBuffer = __webpack_require__(615); var toString = Object.prototype.toString; /** @@ -73456,7 +73482,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 664 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73469,8 +73495,8 @@ module.exports = function kindOf(val) { -var copy = __webpack_require__(665); -var define = __webpack_require__(657); +var copy = __webpack_require__(666); +var define = __webpack_require__(658); var util = __webpack_require__(113); /** @@ -73553,15 +73579,15 @@ module.exports = extend; /***/ }), -/* 665 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(666); -var copyDescriptor = __webpack_require__(667); -var define = __webpack_require__(657); +var typeOf = __webpack_require__(667); +var copyDescriptor = __webpack_require__(668); +var define = __webpack_require__(658); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -73734,10 +73760,10 @@ module.exports.has = has; /***/ }), -/* 666 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(614); +var isBuffer = __webpack_require__(615); var toString = Object.prototype.toString; /** @@ -73856,7 +73882,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 667 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73944,16 +73970,16 @@ function isObject(val) { /***/ }), -/* 668 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(669); -var define = __webpack_require__(657); +var use = __webpack_require__(670); +var define = __webpack_require__(658); var debug = __webpack_require__(205)('snapdragon:compiler'); -var utils = __webpack_require__(671); +var utils = __webpack_require__(672); /** * Create a new `Compiler` with the given `options`. @@ -74107,7 +74133,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(690); + var sourcemaps = __webpack_require__(691); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -74128,7 +74154,7 @@ module.exports = Compiler; /***/ }), -/* 669 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74141,7 +74167,7 @@ module.exports = Compiler; -var utils = __webpack_require__(670); +var utils = __webpack_require__(671); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -74256,7 +74282,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 670 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74270,8 +74296,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(657); -utils.isObject = __webpack_require__(590); +utils.define = __webpack_require__(658); +utils.isObject = __webpack_require__(591); utils.isString = function(val) { @@ -74286,7 +74312,7 @@ module.exports = utils; /***/ }), -/* 671 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74296,9 +74322,9 @@ module.exports = utils; * Module dependencies */ -exports.extend = __webpack_require__(641); -exports.SourceMap = __webpack_require__(672); -exports.sourceMapResolve = __webpack_require__(683); +exports.extend = __webpack_require__(642); +exports.SourceMap = __webpack_require__(673); +exports.sourceMapResolve = __webpack_require__(684); /** * Convert backslash in the given string to forward slashes @@ -74341,7 +74367,7 @@ exports.last = function(arr, n) { /***/ }), -/* 672 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -74349,13 +74375,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(673).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(679).SourceMapConsumer; -exports.SourceNode = __webpack_require__(682).SourceNode; +exports.SourceMapGenerator = __webpack_require__(674).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(680).SourceMapConsumer; +exports.SourceNode = __webpack_require__(683).SourceNode; /***/ }), -/* 673 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -74365,10 +74391,10 @@ exports.SourceNode = __webpack_require__(682).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(674); -var util = __webpack_require__(676); -var ArraySet = __webpack_require__(677).ArraySet; -var MappingList = __webpack_require__(678).MappingList; +var base64VLQ = __webpack_require__(675); +var util = __webpack_require__(677); +var ArraySet = __webpack_require__(678).ArraySet; +var MappingList = __webpack_require__(679).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -74777,7 +74803,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 674 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -74817,7 +74843,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(675); +var base64 = __webpack_require__(676); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -74923,7 +74949,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 675 */ +/* 676 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -74996,7 +75022,7 @@ exports.decode = function (charCode) { /***/ }), -/* 676 */ +/* 677 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -75419,7 +75445,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 677 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -75429,7 +75455,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(676); +var util = __webpack_require__(677); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -75546,7 +75572,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 678 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -75556,7 +75582,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(676); +var util = __webpack_require__(677); /** * Determine whether mappingB is after mappingA with respect to generated @@ -75631,7 +75657,7 @@ exports.MappingList = MappingList; /***/ }), -/* 679 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -75641,11 +75667,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(676); -var binarySearch = __webpack_require__(680); -var ArraySet = __webpack_require__(677).ArraySet; -var base64VLQ = __webpack_require__(674); -var quickSort = __webpack_require__(681).quickSort; +var util = __webpack_require__(677); +var binarySearch = __webpack_require__(681); +var ArraySet = __webpack_require__(678).ArraySet; +var base64VLQ = __webpack_require__(675); +var quickSort = __webpack_require__(682).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -76719,7 +76745,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 680 */ +/* 681 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -76836,7 +76862,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 681 */ +/* 682 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -76956,7 +76982,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 682 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -76966,8 +76992,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(673).SourceMapGenerator; -var util = __webpack_require__(676); +var SourceMapGenerator = __webpack_require__(674).SourceMapGenerator; +var util = __webpack_require__(677); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -77375,17 +77401,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 683 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(684) -var resolveUrl = __webpack_require__(685) -var decodeUriComponent = __webpack_require__(686) -var urix = __webpack_require__(688) -var atob = __webpack_require__(689) +var sourceMappingURL = __webpack_require__(685) +var resolveUrl = __webpack_require__(686) +var decodeUriComponent = __webpack_require__(687) +var urix = __webpack_require__(689) +var atob = __webpack_require__(690) @@ -77683,7 +77709,7 @@ module.exports = { /***/ }), -/* 684 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -77746,7 +77772,7 @@ void (function(root, factory) { /***/ }), -/* 685 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -77764,13 +77790,13 @@ module.exports = resolveUrl /***/ }), -/* 686 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(687) +var decodeUriComponent = __webpack_require__(688) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -77781,7 +77807,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 687 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77882,7 +77908,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 688 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -77905,7 +77931,7 @@ module.exports = urix /***/ }), -/* 689 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77919,7 +77945,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 690 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77927,8 +77953,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(132); var path = __webpack_require__(4); -var define = __webpack_require__(657); -var utils = __webpack_require__(671); +var define = __webpack_require__(658); +var utils = __webpack_require__(672); /** * Expose `mixin()`. @@ -78071,19 +78097,19 @@ exports.comment = function(node) { /***/ }), -/* 691 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(669); +var use = __webpack_require__(670); var util = __webpack_require__(113); -var Cache = __webpack_require__(692); -var define = __webpack_require__(657); +var Cache = __webpack_require__(693); +var define = __webpack_require__(658); var debug = __webpack_require__(205)('snapdragon:parser'); -var Position = __webpack_require__(693); -var utils = __webpack_require__(671); +var Position = __webpack_require__(694); +var utils = __webpack_require__(672); /** * Create a new `Parser` with the given `input` and `options`. @@ -78611,7 +78637,7 @@ module.exports = Parser; /***/ }), -/* 692 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78718,13 +78744,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 693 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(657); +var define = __webpack_require__(658); /** * Store position for a node @@ -78739,14 +78765,14 @@ module.exports = function Position(start, parser) { /***/ }), -/* 694 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(695); -var assignSymbols = __webpack_require__(598); +var isExtendable = __webpack_require__(696); +var assignSymbols = __webpack_require__(599); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -78806,7 +78832,7 @@ function isEnum(obj, key) { /***/ }), -/* 695 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78819,7 +78845,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(597); +var isPlainObject = __webpack_require__(598); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -78827,14 +78853,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 696 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(697); -var extglob = __webpack_require__(711); +var nanomatch = __webpack_require__(698); +var extglob = __webpack_require__(712); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -78911,7 +78937,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 697 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78922,17 +78948,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(113); -var toRegex = __webpack_require__(582); -var extend = __webpack_require__(698); +var toRegex = __webpack_require__(583); +var extend = __webpack_require__(699); /** * Local dependencies */ -var compilers = __webpack_require__(700); -var parsers = __webpack_require__(701); -var cache = __webpack_require__(704); -var utils = __webpack_require__(706); +var compilers = __webpack_require__(701); +var parsers = __webpack_require__(702); +var cache = __webpack_require__(705); +var utils = __webpack_require__(707); var MAX_LENGTH = 1024 * 64; /** @@ -79756,14 +79782,14 @@ module.exports = nanomatch; /***/ }), -/* 698 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(699); -var assignSymbols = __webpack_require__(598); +var isExtendable = __webpack_require__(700); +var assignSymbols = __webpack_require__(599); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -79823,7 +79849,7 @@ function isEnum(obj, key) { /***/ }), -/* 699 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79836,7 +79862,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(597); +var isPlainObject = __webpack_require__(598); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -79844,7 +79870,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 700 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80190,15 +80216,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 701 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(599); -var toRegex = __webpack_require__(582); -var isOdd = __webpack_require__(702); +var regexNot = __webpack_require__(600); +var toRegex = __webpack_require__(583); +var isOdd = __webpack_require__(703); /** * Characters to use in negation regex (we want to "not" match @@ -80584,7 +80610,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 702 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80597,7 +80623,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(703); +var isNumber = __webpack_require__(704); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -80611,7 +80637,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 703 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80639,14 +80665,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 704 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(705))(); +module.exports = new (__webpack_require__(706))(); /***/ }), -/* 705 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80659,7 +80685,7 @@ module.exports = new (__webpack_require__(705))(); -var MapCache = __webpack_require__(692); +var MapCache = __webpack_require__(693); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -80781,7 +80807,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 706 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80794,14 +80820,14 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(707)(); -var Snapdragon = __webpack_require__(626); -utils.define = __webpack_require__(708); -utils.diff = __webpack_require__(709); -utils.extend = __webpack_require__(698); -utils.pick = __webpack_require__(710); -utils.typeOf = __webpack_require__(592); -utils.unique = __webpack_require__(602); +var isWindows = __webpack_require__(708)(); +var Snapdragon = __webpack_require__(627); +utils.define = __webpack_require__(709); +utils.diff = __webpack_require__(710); +utils.extend = __webpack_require__(699); +utils.pick = __webpack_require__(711); +utils.typeOf = __webpack_require__(593); +utils.unique = __webpack_require__(603); /** * Returns true if the given value is effectively an empty string @@ -81167,7 +81193,7 @@ utils.unixify = function(options) { /***/ }), -/* 707 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -81195,7 +81221,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 708 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81208,8 +81234,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(590); -var isDescriptor = __webpack_require__(591); +var isobject = __webpack_require__(591); +var isDescriptor = __webpack_require__(592); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -81240,7 +81266,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 709 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81294,7 +81320,7 @@ function diffArray(one, two) { /***/ }), -/* 710 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81307,7 +81333,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(590); +var isObject = __webpack_require__(591); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -81336,7 +81362,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 711 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81346,18 +81372,18 @@ module.exports = function pick(obj, keys) { * Module dependencies */ -var extend = __webpack_require__(641); -var unique = __webpack_require__(602); -var toRegex = __webpack_require__(582); +var extend = __webpack_require__(642); +var unique = __webpack_require__(603); +var toRegex = __webpack_require__(583); /** * Local dependencies */ -var compilers = __webpack_require__(712); -var parsers = __webpack_require__(718); -var Extglob = __webpack_require__(721); -var utils = __webpack_require__(720); +var compilers = __webpack_require__(713); +var parsers = __webpack_require__(719); +var Extglob = __webpack_require__(722); +var utils = __webpack_require__(721); var MAX_LENGTH = 1024 * 64; /** @@ -81674,13 +81700,13 @@ module.exports = extglob; /***/ }), -/* 712 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(713); +var brackets = __webpack_require__(714); /** * Extglob compilers @@ -81850,7 +81876,7 @@ module.exports = function(extglob) { /***/ }), -/* 713 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81860,17 +81886,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(714); -var parsers = __webpack_require__(716); +var compilers = __webpack_require__(715); +var parsers = __webpack_require__(717); /** * Module dependencies */ var debug = __webpack_require__(205)('expand-brackets'); -var extend = __webpack_require__(641); -var Snapdragon = __webpack_require__(626); -var toRegex = __webpack_require__(582); +var extend = __webpack_require__(642); +var Snapdragon = __webpack_require__(627); +var toRegex = __webpack_require__(583); /** * Parses the given POSIX character class `pattern` and returns a @@ -82068,13 +82094,13 @@ module.exports = brackets; /***/ }), -/* 714 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(715); +var posix = __webpack_require__(716); module.exports = function(brackets) { brackets.compiler @@ -82162,7 +82188,7 @@ module.exports = function(brackets) { /***/ }), -/* 715 */ +/* 716 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82191,14 +82217,14 @@ module.exports = { /***/ }), -/* 716 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(717); -var define = __webpack_require__(657); +var utils = __webpack_require__(718); +var define = __webpack_require__(658); /** * Text regex @@ -82417,14 +82443,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 717 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(582); -var regexNot = __webpack_require__(599); +var toRegex = __webpack_require__(583); +var regexNot = __webpack_require__(600); var cached; /** @@ -82458,15 +82484,15 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 718 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(713); -var define = __webpack_require__(719); -var utils = __webpack_require__(720); +var brackets = __webpack_require__(714); +var define = __webpack_require__(720); +var utils = __webpack_require__(721); /** * Characters to use in text regex (we want to "not" match @@ -82621,7 +82647,7 @@ module.exports = parsers; /***/ }), -/* 719 */ +/* 720 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82634,7 +82660,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(591); +var isDescriptor = __webpack_require__(592); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -82659,14 +82685,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 720 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(599); -var Cache = __webpack_require__(705); +var regex = __webpack_require__(600); +var Cache = __webpack_require__(706); /** * Utils @@ -82735,7 +82761,7 @@ utils.createRegex = function(str) { /***/ }), -/* 721 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82745,16 +82771,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(626); -var define = __webpack_require__(719); -var extend = __webpack_require__(641); +var Snapdragon = __webpack_require__(627); +var define = __webpack_require__(720); +var extend = __webpack_require__(642); /** * Local dependencies */ -var compilers = __webpack_require__(712); -var parsers = __webpack_require__(718); +var compilers = __webpack_require__(713); +var parsers = __webpack_require__(719); /** * Customize Snapdragon parser and renderer @@ -82820,16 +82846,16 @@ module.exports = Extglob; /***/ }), -/* 722 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(711); -var nanomatch = __webpack_require__(697); -var regexNot = __webpack_require__(599); -var toRegex = __webpack_require__(582); +var extglob = __webpack_require__(712); +var nanomatch = __webpack_require__(698); +var regexNot = __webpack_require__(600); +var toRegex = __webpack_require__(583); var not; /** @@ -82910,14 +82936,14 @@ function textRegex(pattern) { /***/ }), -/* 723 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(705))(); +module.exports = new (__webpack_require__(706))(); /***/ }), -/* 724 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82930,13 +82956,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(626); -utils.define = __webpack_require__(725); -utils.diff = __webpack_require__(709); -utils.extend = __webpack_require__(694); -utils.pick = __webpack_require__(710); -utils.typeOf = __webpack_require__(592); -utils.unique = __webpack_require__(602); +var Snapdragon = __webpack_require__(627); +utils.define = __webpack_require__(726); +utils.diff = __webpack_require__(710); +utils.extend = __webpack_require__(695); +utils.pick = __webpack_require__(711); +utils.typeOf = __webpack_require__(593); +utils.unique = __webpack_require__(603); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -83233,7 +83259,7 @@ utils.unixify = function(options) { /***/ }), -/* 725 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83246,8 +83272,8 @@ utils.unixify = function(options) { -var isobject = __webpack_require__(590); -var isDescriptor = __webpack_require__(591); +var isobject = __webpack_require__(591); +var isDescriptor = __webpack_require__(592); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -83278,7 +83304,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 726 */ +/* 727 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83297,9 +83323,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(727); -var reader_1 = __webpack_require__(740); -var fs_stream_1 = __webpack_require__(744); +var readdir = __webpack_require__(728); +var reader_1 = __webpack_require__(741); +var fs_stream_1 = __webpack_require__(745); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -83360,15 +83386,15 @@ exports.default = ReaderAsync; /***/ }), -/* 727 */ +/* 728 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(728); -const readdirAsync = __webpack_require__(736); -const readdirStream = __webpack_require__(739); +const readdirSync = __webpack_require__(729); +const readdirAsync = __webpack_require__(737); +const readdirStream = __webpack_require__(740); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -83452,7 +83478,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 728 */ +/* 729 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83460,11 +83486,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(729); +const DirectoryReader = __webpack_require__(730); let syncFacade = { - fs: __webpack_require__(734), - forEach: __webpack_require__(735), + fs: __webpack_require__(735), + forEach: __webpack_require__(736), sync: true }; @@ -83493,7 +83519,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 729 */ +/* 730 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83502,9 +83528,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(173).Readable; const EventEmitter = __webpack_require__(164).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(730); -const stat = __webpack_require__(732); -const call = __webpack_require__(733); +const normalizeOptions = __webpack_require__(731); +const stat = __webpack_require__(733); +const call = __webpack_require__(734); /** * Asynchronously reads the contents of a directory and streams the results @@ -83880,14 +83906,14 @@ module.exports = DirectoryReader; /***/ }), -/* 730 */ +/* 731 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(731); +const globToRegExp = __webpack_require__(732); module.exports = normalizeOptions; @@ -84064,7 +84090,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 731 */ +/* 732 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -84201,13 +84227,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 732 */ +/* 733 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(733); +const call = __webpack_require__(734); module.exports = stat; @@ -84282,7 +84308,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 733 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84343,14 +84369,14 @@ function callOnce (fn) { /***/ }), -/* 734 */ +/* 735 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const call = __webpack_require__(733); +const call = __webpack_require__(734); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -84414,7 +84440,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 735 */ +/* 736 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84443,7 +84469,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 736 */ +/* 737 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84451,12 +84477,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(737); -const DirectoryReader = __webpack_require__(729); +const maybe = __webpack_require__(738); +const DirectoryReader = __webpack_require__(730); let asyncFacade = { fs: __webpack_require__(132), - forEach: __webpack_require__(738), + forEach: __webpack_require__(739), async: true }; @@ -84498,7 +84524,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 737 */ +/* 738 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84525,7 +84551,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 738 */ +/* 739 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84561,7 +84587,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 739 */ +/* 740 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84569,11 +84595,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(729); +const DirectoryReader = __webpack_require__(730); let streamFacade = { fs: __webpack_require__(132), - forEach: __webpack_require__(738), + forEach: __webpack_require__(739), async: true }; @@ -84593,16 +84619,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 740 */ +/* 741 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var deep_1 = __webpack_require__(741); -var entry_1 = __webpack_require__(743); -var pathUtil = __webpack_require__(742); +var deep_1 = __webpack_require__(742); +var entry_1 = __webpack_require__(744); +var pathUtil = __webpack_require__(743); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -84668,14 +84694,14 @@ exports.default = Reader; /***/ }), -/* 741 */ +/* 742 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(742); -var patternUtils = __webpack_require__(576); +var pathUtils = __webpack_require__(743); +var patternUtils = __webpack_require__(577); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -84758,7 +84784,7 @@ exports.default = DeepFilter; /***/ }), -/* 742 */ +/* 743 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84789,14 +84815,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 743 */ +/* 744 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(742); -var patternUtils = __webpack_require__(576); +var pathUtils = __webpack_require__(743); +var patternUtils = __webpack_require__(577); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -84881,7 +84907,7 @@ exports.default = EntryFilter; /***/ }), -/* 744 */ +/* 745 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84901,8 +84927,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(173); -var fsStat = __webpack_require__(745); -var fs_1 = __webpack_require__(749); +var fsStat = __webpack_require__(746); +var fs_1 = __webpack_require__(750); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -84952,14 +84978,14 @@ exports.default = FileSystemStream; /***/ }), -/* 745 */ +/* 746 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(746); -const statProvider = __webpack_require__(748); +const optionsManager = __webpack_require__(747); +const statProvider = __webpack_require__(749); /** * Asynchronous API. */ @@ -84990,13 +85016,13 @@ exports.statSync = statSync; /***/ }), -/* 746 */ +/* 747 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(747); +const fsAdapter = __webpack_require__(748); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -85009,7 +85035,7 @@ exports.prepare = prepare; /***/ }), -/* 747 */ +/* 748 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85032,7 +85058,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 748 */ +/* 749 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85084,7 +85110,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 749 */ +/* 750 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85115,7 +85141,7 @@ exports.default = FileSystem; /***/ }), -/* 750 */ +/* 751 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85135,9 +85161,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(173); -var readdir = __webpack_require__(727); -var reader_1 = __webpack_require__(740); -var fs_stream_1 = __webpack_require__(744); +var readdir = __webpack_require__(728); +var reader_1 = __webpack_require__(741); +var fs_stream_1 = __webpack_require__(745); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -85205,7 +85231,7 @@ exports.default = ReaderStream; /***/ }), -/* 751 */ +/* 752 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85224,9 +85250,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(727); -var reader_1 = __webpack_require__(740); -var fs_sync_1 = __webpack_require__(752); +var readdir = __webpack_require__(728); +var reader_1 = __webpack_require__(741); +var fs_sync_1 = __webpack_require__(753); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -85286,7 +85312,7 @@ exports.default = ReaderSync; /***/ }), -/* 752 */ +/* 753 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85305,8 +85331,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(745); -var fs_1 = __webpack_require__(749); +var fsStat = __webpack_require__(746); +var fs_1 = __webpack_require__(750); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -85352,7 +85378,7 @@ exports.default = FileSystemSync; /***/ }), -/* 753 */ +/* 754 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85368,7 +85394,7 @@ exports.flatten = flatten; /***/ }), -/* 754 */ +/* 755 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85389,13 +85415,13 @@ exports.merge = merge; /***/ }), -/* 755 */ +/* 756 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(756); +const pathType = __webpack_require__(757); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -85461,13 +85487,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 756 */ +/* 757 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const pify = __webpack_require__(757); +const pify = __webpack_require__(758); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -85510,7 +85536,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 757 */ +/* 758 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85601,17 +85627,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 758 */ +/* 759 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(572); -const gitIgnore = __webpack_require__(759); +const fastGlob = __webpack_require__(573); +const gitIgnore = __webpack_require__(760); const pify = __webpack_require__(404); -const slash = __webpack_require__(760); +const slash = __webpack_require__(761); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -85709,7 +85735,7 @@ module.exports.sync = options => { /***/ }), -/* 759 */ +/* 760 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -86178,7 +86204,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 760 */ +/* 761 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86196,7 +86222,7 @@ module.exports = input => { /***/ }), -/* 761 */ +/* 762 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86209,7 +86235,7 @@ module.exports = input => { -var isGlob = __webpack_require__(762); +var isGlob = __webpack_require__(763); module.exports = function hasGlob(val) { if (val == null) return false; @@ -86229,7 +86255,7 @@ module.exports = function hasGlob(val) { /***/ }), -/* 762 */ +/* 763 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -86260,17 +86286,17 @@ module.exports = function isGlob(str) { /***/ }), -/* 763 */ +/* 764 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const {constants: fsConstants} = __webpack_require__(132); -const pEvent = __webpack_require__(764); -const CpFileError = __webpack_require__(767); -const fs = __webpack_require__(769); -const ProgressEmitter = __webpack_require__(772); +const pEvent = __webpack_require__(765); +const CpFileError = __webpack_require__(768); +const fs = __webpack_require__(770); +const ProgressEmitter = __webpack_require__(773); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -86384,12 +86410,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 764 */ +/* 765 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(765); +const pTimeout = __webpack_require__(766); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -86680,12 +86706,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 765 */ +/* 766 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(766); +const pFinally = __webpack_require__(767); class TimeoutError extends Error { constructor(message) { @@ -86731,7 +86757,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 766 */ +/* 767 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86753,12 +86779,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 767 */ +/* 768 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(768); +const NestedError = __webpack_require__(769); class CpFileError extends NestedError { constructor(message, nested) { @@ -86772,7 +86798,7 @@ module.exports = CpFileError; /***/ }), -/* 768 */ +/* 769 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(113).inherits; @@ -86828,16 +86854,16 @@ module.exports = NestedError; /***/ }), -/* 769 */ +/* 770 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(113); const fs = __webpack_require__(233); -const makeDir = __webpack_require__(770); -const pEvent = __webpack_require__(764); -const CpFileError = __webpack_require__(767); +const makeDir = __webpack_require__(771); +const pEvent = __webpack_require__(765); +const CpFileError = __webpack_require__(768); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -86934,7 +86960,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 770 */ +/* 771 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86942,7 +86968,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(132); const path = __webpack_require__(4); const {promisify} = __webpack_require__(113); -const semver = __webpack_require__(771); +const semver = __webpack_require__(772); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -87097,7 +87123,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 771 */ +/* 772 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -88699,7 +88725,7 @@ function coerce (version, options) { /***/ }), -/* 772 */ +/* 773 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88740,7 +88766,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 773 */ +/* 774 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88786,12 +88812,12 @@ exports.default = module.exports; /***/ }), -/* 774 */ +/* 775 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(775); +const pMap = __webpack_require__(776); const pFilter = async (iterable, filterer, options) => { const values = await pMap( @@ -88808,7 +88834,7 @@ module.exports.default = pFilter; /***/ }), -/* 775 */ +/* 776 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88887,12 +88913,12 @@ module.exports.default = pMap; /***/ }), -/* 776 */ +/* 777 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(768); +const NestedError = __webpack_require__(769); class CpyError extends NestedError { constructor(message, nested) { @@ -88906,7 +88932,7 @@ module.exports = CpyError; /***/ }), -/* 777 */ +/* 778 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88914,10 +88940,10 @@ module.exports = CpyError; const fs = __webpack_require__(132); const arrayUnion = __webpack_require__(242); const merge2 = __webpack_require__(243); -const fastGlob = __webpack_require__(778); +const fastGlob = __webpack_require__(779); const dirGlob = __webpack_require__(326); -const gitignore = __webpack_require__(803); -const {FilterStream, UniqueStream} = __webpack_require__(804); +const gitignore = __webpack_require__(804); +const {FilterStream, UniqueStream} = __webpack_require__(805); const DEFAULT_FILTER = () => false; @@ -89094,17 +89120,17 @@ module.exports.gitignore = gitignore; /***/ }), -/* 778 */ +/* 779 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__(779); -const async_1 = __webpack_require__(789); -const stream_1 = __webpack_require__(799); -const sync_1 = __webpack_require__(800); -const settings_1 = __webpack_require__(802); -const utils = __webpack_require__(780); +const taskManager = __webpack_require__(780); +const async_1 = __webpack_require__(790); +const stream_1 = __webpack_require__(800); +const sync_1 = __webpack_require__(801); +const settings_1 = __webpack_require__(803); +const utils = __webpack_require__(781); async function FastGlob(source, options) { assertPatternsInput(source); const works = getWorks(source, async_1.default, options); @@ -89168,14 +89194,14 @@ module.exports = FastGlob; /***/ }), -/* 779 */ +/* 780 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; -const utils = __webpack_require__(780); +const utils = __webpack_require__(781); function generate(patterns, settings) { const positivePatterns = getPositivePatterns(patterns); const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); @@ -89255,31 +89281,31 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 780 */ +/* 781 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; -const array = __webpack_require__(781); +const array = __webpack_require__(782); exports.array = array; -const errno = __webpack_require__(782); +const errno = __webpack_require__(783); exports.errno = errno; -const fs = __webpack_require__(783); +const fs = __webpack_require__(784); exports.fs = fs; -const path = __webpack_require__(784); +const path = __webpack_require__(785); exports.path = path; -const pattern = __webpack_require__(785); +const pattern = __webpack_require__(786); exports.pattern = pattern; -const stream = __webpack_require__(787); +const stream = __webpack_require__(788); exports.stream = stream; -const string = __webpack_require__(788); +const string = __webpack_require__(789); exports.string = string; /***/ }), -/* 781 */ +/* 782 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89308,7 +89334,7 @@ exports.splitWhen = splitWhen; /***/ }), -/* 782 */ +/* 783 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89322,7 +89348,7 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 783 */ +/* 784 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89348,7 +89374,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 784 */ +/* 785 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89388,7 +89414,7 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), -/* 785 */ +/* 786 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89397,7 +89423,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; const path = __webpack_require__(4); const globParent = __webpack_require__(265); -const micromatch = __webpack_require__(786); +const micromatch = __webpack_require__(787); const GLOBSTAR = '**'; const ESCAPE_SYMBOL = '\\'; const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; @@ -89552,7 +89578,7 @@ exports.matchAny = matchAny; /***/ }), -/* 786 */ +/* 787 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90026,7 +90052,7 @@ module.exports = micromatch; /***/ }), -/* 787 */ +/* 788 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90050,7 +90076,7 @@ function propagateCloseEventToSources(streams) { /***/ }), -/* 788 */ +/* 789 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90068,14 +90094,14 @@ exports.isEmpty = isEmpty; /***/ }), -/* 789 */ +/* 790 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(790); -const provider_1 = __webpack_require__(792); +const stream_1 = __webpack_require__(791); +const provider_1 = __webpack_require__(793); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -90103,7 +90129,7 @@ exports.default = ProviderAsync; /***/ }), -/* 790 */ +/* 791 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90112,7 +90138,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(173); const fsStat = __webpack_require__(289); const fsWalk = __webpack_require__(294); -const reader_1 = __webpack_require__(791); +const reader_1 = __webpack_require__(792); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -90165,7 +90191,7 @@ exports.default = ReaderStream; /***/ }), -/* 791 */ +/* 792 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90173,7 +90199,7 @@ exports.default = ReaderStream; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); const fsStat = __webpack_require__(289); -const utils = __webpack_require__(780); +const utils = __webpack_require__(781); class Reader { constructor(_settings) { this._settings = _settings; @@ -90205,17 +90231,17 @@ exports.default = Reader; /***/ }), -/* 792 */ +/* 793 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const deep_1 = __webpack_require__(793); -const entry_1 = __webpack_require__(796); -const error_1 = __webpack_require__(797); -const entry_2 = __webpack_require__(798); +const deep_1 = __webpack_require__(794); +const entry_1 = __webpack_require__(797); +const error_1 = __webpack_require__(798); +const entry_2 = __webpack_require__(799); class Provider { constructor(_settings) { this._settings = _settings; @@ -90260,14 +90286,14 @@ exports.default = Provider; /***/ }), -/* 793 */ +/* 794 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(780); -const partial_1 = __webpack_require__(794); +const utils = __webpack_require__(781); +const partial_1 = __webpack_require__(795); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -90329,13 +90355,13 @@ exports.default = DeepFilter; /***/ }), -/* 794 */ +/* 795 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(795); +const matcher_1 = __webpack_require__(796); class PartialMatcher extends matcher_1.default { match(filepath) { const parts = filepath.split('/'); @@ -90374,13 +90400,13 @@ exports.default = PartialMatcher; /***/ }), -/* 795 */ +/* 796 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(780); +const utils = __webpack_require__(781); class Matcher { constructor(_patterns, _settings, _micromatchOptions) { this._patterns = _patterns; @@ -90431,13 +90457,13 @@ exports.default = Matcher; /***/ }), -/* 796 */ +/* 797 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(780); +const utils = __webpack_require__(781); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -90494,13 +90520,13 @@ exports.default = EntryFilter; /***/ }), -/* 797 */ +/* 798 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(780); +const utils = __webpack_require__(781); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -90516,13 +90542,13 @@ exports.default = ErrorFilter; /***/ }), -/* 798 */ +/* 799 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(780); +const utils = __webpack_require__(781); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -90549,15 +90575,15 @@ exports.default = EntryTransformer; /***/ }), -/* 799 */ +/* 800 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(173); -const stream_2 = __webpack_require__(790); -const provider_1 = __webpack_require__(792); +const stream_2 = __webpack_require__(791); +const provider_1 = __webpack_require__(793); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -90587,14 +90613,14 @@ exports.default = ProviderStream; /***/ }), -/* 800 */ +/* 801 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(801); -const provider_1 = __webpack_require__(792); +const sync_1 = __webpack_require__(802); +const provider_1 = __webpack_require__(793); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -90617,7 +90643,7 @@ exports.default = ProviderSync; /***/ }), -/* 801 */ +/* 802 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90625,7 +90651,7 @@ exports.default = ProviderSync; Object.defineProperty(exports, "__esModule", { value: true }); const fsStat = __webpack_require__(289); const fsWalk = __webpack_require__(294); -const reader_1 = __webpack_require__(791); +const reader_1 = __webpack_require__(792); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -90667,7 +90693,7 @@ exports.default = ReaderSync; /***/ }), -/* 802 */ +/* 803 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90731,7 +90757,7 @@ exports.default = Settings; /***/ }), -/* 803 */ +/* 804 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90739,7 +90765,7 @@ exports.default = Settings; const {promisify} = __webpack_require__(113); const fs = __webpack_require__(132); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(778); +const fastGlob = __webpack_require__(779); const gitIgnore = __webpack_require__(329); const slash = __webpack_require__(330); @@ -90858,7 +90884,7 @@ module.exports.sync = options => { /***/ }), -/* 804 */ +/* 805 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90911,7 +90937,7 @@ module.exports = { /***/ }), -/* 805 */ +/* 806 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -90919,13 +90945,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return buildNonBazelProductionProjects; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProductionProjects", function() { return getProductionProjects; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProject", function() { return buildProject; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(565); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(566); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(562); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(563); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(343); diff --git a/packages/kbn-pm/src/utils/sort_package_json.ts b/packages/kbn-pm/src/utils/sort_package_json.ts index b4df5355744f1..84a1ed6d80960 100644 --- a/packages/kbn-pm/src/utils/sort_package_json.ts +++ b/packages/kbn-pm/src/utils/sort_package_json.ts @@ -7,41 +7,10 @@ */ import Fs from 'fs/promises'; - -import sorter from 'sort-package-json'; - +import { sortPackageJson as sort } from '@kbn/dev-utils/sort_package_json'; import { Kibana } from './kibana'; export async function sortPackageJson(kbn: Kibana) { const packageJsonPath = kbn.getAbsolute('package.json'); - const packageJson = await Fs.readFile(packageJsonPath, 'utf-8'); - await Fs.writeFile( - packageJsonPath, - JSON.stringify( - sorter(JSON.parse(packageJson), { - // top level keys in the order they were written when this was implemented - sortOrder: [ - 'name', - 'description', - 'keywords', - 'private', - 'version', - 'branch', - 'types', - 'tsdocMetadata', - 'build', - 'homepage', - 'bugs', - 'kibana', - 'author', - 'scripts', - 'repository', - 'engines', - 'resolutions', - ], - }), - null, - 2 - ) + '\n' - ); + await Fs.writeFile(packageJsonPath, sort(await Fs.readFile(packageJsonPath, 'utf-8'))); } diff --git a/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts b/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts index 7a0d9be4629cd..da82aef8f7d79 100644 --- a/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts +++ b/packages/kbn-type-summarizer/src/lib/bazel_cli_config.ts @@ -12,7 +12,7 @@ import { CliError } from './cli_error'; import { parseCliFlags } from './cli_flags'; import * as Path from './path'; -const TYPE_SUMMARIZER_PACKAGES = ['@kbn/type-summarizer', '@kbn/crypto']; +const TYPE_SUMMARIZER_PACKAGES = ['@kbn/type-summarizer', '@kbn/crypto', '@kbn/generate']; const isString = (i: any): i is string => typeof i === 'string' && i.length > 0; diff --git a/scripts/generate.js b/scripts/generate.js new file mode 100644 index 0000000000000..29774e8088d65 --- /dev/null +++ b/scripts/generate.js @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +require('../src/setup_node_env/ensure_node_preserve_symlinks'); +require('source-map-support/register'); +require('@kbn/generate').runGenerateCli(); diff --git a/src/dev/file.ts b/src/dev/file.ts index b532a7bb70602..16d64d8c0c218 100644 --- a/src/dev/file.ts +++ b/src/dev/file.ts @@ -50,9 +50,17 @@ export class File { } public isFixture() { - return ( - this.relativePath.split(sep).includes('__fixtures__') || this.path.endsWith('.test-d.ts') - ); + const parts = this.relativePath.split(sep); + if (parts.includes('__fixtures__') || this.path.endsWith('.test-d.ts')) { + return true; + } + + const i = parts.indexOf('kbn-generate'); + if (i >= 0 && parts[i + 1] === 'templates') { + return true; + } + + return false; } public getRelativeParentDirs() { diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 932fdaf6c2e28..86448be7c3e53 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -107,7 +107,10 @@ export const IGNORE_DIRECTORY_GLOBS = [ * * @type {Array} */ -export const REMOVE_EXTENSION = ['packages/kbn-plugin-generator/template/**/*.ejs']; +export const REMOVE_EXTENSION = [ + 'packages/kbn-plugin-generator/template/**/*.ejs', + 'packages/kbn-generate/templates/**/*.ejs', +]; /** * DO NOT ADD FILES TO THIS LIST!! diff --git a/yarn.lock b/yarn.lock index 090b6f8cfc0b5..8c2b1f60503d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2989,6 +2989,10 @@ version "0.0.0" uid "" +"@kbn/generate@link:bazel-bin/packages/kbn-generate": + version "0.0.0" + uid "" + "@kbn/i18n-react@link:bazel-bin/packages/kbn-i18n-react": version "0.0.0" uid "" @@ -3025,6 +3029,10 @@ version "0.0.0" uid "" +"@kbn/packages@link:bazel-bin/packages/kbn-packages": + version "0.0.0" + uid "" + "@kbn/plugin-generator@link:bazel-bin/packages/kbn-plugin-generator": version "0.0.0" uid "" @@ -5954,6 +5962,10 @@ version "0.0.0" uid "" +"@types/kbn__generate@link:bazel-bin/packages/kbn-generate/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__i18n-react@link:bazel-bin/packages/kbn-i18n-react/npm_module_types": version "0.0.0" uid "" @@ -5990,6 +6002,10 @@ version "0.0.0" uid "" +"@types/kbn__packages@link:bazel-bin/packages/kbn-packages/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__plugin-generator@link:bazel-bin/packages/kbn-plugin-generator/npm_module_types": version "0.0.0" uid "" From f89d5313973067ef0f26ca5213b07cbd29820c36 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 9 Mar 2022 04:05:32 +0000 Subject: [PATCH 089/140] skip flaky suite (#123372) --- test/functional/apps/discover/_runtime_fields_editor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_runtime_fields_editor.ts b/test/functional/apps/discover/_runtime_fields_editor.ts index 8406c75523854..7c884f8b759dc 100644 --- a/test/functional/apps/discover/_runtime_fields_editor.ts +++ b/test/functional/apps/discover/_runtime_fields_editor.ts @@ -32,7 +32,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); }; - describe('discover integration with runtime fields editor', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/123372 + describe.skip('discover integration with runtime fields editor', function describeIndexTests() { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); From b9b4dc0a8d356fd553e97ed4b118d06ce9c5817f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Wed, 9 Mar 2022 08:00:55 +0100 Subject: [PATCH 090/140] Remove deprecated & unused `SavedObjectsImportFailure.title` (#127043) --- ...n-core-public.savedobjectsimportfailure.md | 1 - ...-public.savedobjectsimportfailure.title.md | 16 ---- ...n-core-server.savedobjectsimportfailure.md | 1 - ...-server.savedobjectsimportfailure.title.md | 16 ---- src/core/public/public.api.md | 2 - .../import/import_saved_objects.test.ts | 1 - .../import/lib/check_conflicts.test.ts | 5 -- .../import/lib/check_conflicts.ts | 4 +- .../import/lib/check_origin_conflicts.test.ts | 2 - .../import/lib/check_origin_conflicts.ts | 2 - .../import/lib/collect_saved_objects.test.ts | 8 +- .../import/lib/collect_saved_objects.ts | 1 - .../import/lib/extract_errors.test.ts | 77 +++++++++---------- .../import/lib/extract_errors.ts | 2 - .../import/lib/validate_references.ts | 1 - .../import/resolve_import_errors.test.ts | 1 - src/core/server/saved_objects/import/types.ts | 4 - .../routes/integration_tests/import.test.ts | 5 -- src/core/server/server.api.md | 2 - .../apis/saved_objects/import.ts | 3 - .../saved_objects_hidden_type/import.ts | 1 - .../resolve_import_errors.ts | 1 - .../common/suites/copy_to_space.ts | 7 -- .../suites/resolve_copy_to_space_conflicts.ts | 3 - 24 files changed, 43 insertions(+), 123 deletions(-) delete mode 100644 docs/development/core/public/kibana-plugin-core-public.savedobjectsimportfailure.title.md delete mode 100644 docs/development/core/server/kibana-plugin-core-server.savedobjectsimportfailure.title.md diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportfailure.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportfailure.md index be1a20b3c71a4..e7de1014bdaf2 100644 --- a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportfailure.md +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportfailure.md @@ -20,6 +20,5 @@ export interface SavedObjectsImportFailure | [id](./kibana-plugin-core-public.savedobjectsimportfailure.id.md) | string | | | [meta](./kibana-plugin-core-public.savedobjectsimportfailure.meta.md) | { title?: string; icon?: string; } | | | [overwrite?](./kibana-plugin-core-public.savedobjectsimportfailure.overwrite.md) | boolean | (Optional) If overwrite is specified, an attempt was made to overwrite an existing object. | -| [title?](./kibana-plugin-core-public.savedobjectsimportfailure.title.md) | string | (Optional) | | [type](./kibana-plugin-core-public.savedobjectsimportfailure.type.md) | string | | diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportfailure.title.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportfailure.title.md deleted file mode 100644 index 0024358bda030..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportfailure.title.md +++ /dev/null @@ -1,16 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportFailure](./kibana-plugin-core-public.savedobjectsimportfailure.md) > [title](./kibana-plugin-core-public.savedobjectsimportfailure.title.md) - -## SavedObjectsImportFailure.title property - -> Warning: This API is now obsolete. -> -> Use `meta.title` instead -> - -Signature: - -```typescript -title?: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportfailure.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportfailure.md index 52db837479cf6..4bdc3d99028ab 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportfailure.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportfailure.md @@ -20,6 +20,5 @@ export interface SavedObjectsImportFailure | [id](./kibana-plugin-core-server.savedobjectsimportfailure.id.md) | string | | | [meta](./kibana-plugin-core-server.savedobjectsimportfailure.meta.md) | { title?: string; icon?: string; } | | | [overwrite?](./kibana-plugin-core-server.savedobjectsimportfailure.overwrite.md) | boolean | (Optional) If overwrite is specified, an attempt was made to overwrite an existing object. | -| [title?](./kibana-plugin-core-server.savedobjectsimportfailure.title.md) | string | (Optional) | | [type](./kibana-plugin-core-server.savedobjectsimportfailure.type.md) | string | | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportfailure.title.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportfailure.title.md deleted file mode 100644 index 12326e6b0e4bb..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportfailure.title.md +++ /dev/null @@ -1,16 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportFailure](./kibana-plugin-core-server.savedobjectsimportfailure.md) > [title](./kibana-plugin-core-server.savedobjectsimportfailure.title.md) - -## SavedObjectsImportFailure.title property - -> Warning: This API is now obsolete. -> -> Use `meta.title` instead -> - -Signature: - -```typescript -title?: string; -``` diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index b30c009bf2538..348d20ea02e52 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -1255,8 +1255,6 @@ export interface SavedObjectsImportFailure { icon?: string; }; overwrite?: boolean; - // @deprecated (undocumented) - title?: string; // (undocumented) type: string; } diff --git a/src/core/server/saved_objects/import/import_saved_objects.test.ts b/src/core/server/saved_objects/import/import_saved_objects.test.ts index 2f31b4cf3ead3..046b9d3505cde 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.test.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.test.ts @@ -112,7 +112,6 @@ describe('#importSavedObjectsFromStream', () => { return { type: 'foo-type', id: uuidv4(), - title: 'some-title', meta: { title }, error: { type: 'conflict' }, }; diff --git a/src/core/server/saved_objects/import/lib/check_conflicts.test.ts b/src/core/server/saved_objects/import/lib/check_conflicts.test.ts index b2de6f11d5cb8..e11e880016550 100644 --- a/src/core/server/saved_objects/import/lib/check_conflicts.test.ts +++ b/src/core/server/saved_objects/import/lib/check_conflicts.test.ts @@ -108,13 +108,11 @@ describe('#checkConflicts', () => { errors: [ { ...obj2Error, - title: obj2.attributes.title, meta: { title: obj2.attributes.title }, error: { type: 'conflict' }, }, { ...obj4Error, - title: obj4.attributes.title, meta: { title: obj4.attributes.title }, error: { ...obj4Error.error, type: 'unknown' }, }, @@ -136,7 +134,6 @@ describe('#checkConflicts', () => { errors: [ { ...obj4Error, - title: obj4.attributes.title, meta: { title: obj4.attributes.title }, error: { ...obj4Error.error, type: 'unknown' }, }, @@ -174,13 +171,11 @@ describe('#checkConflicts', () => { errors: [ { ...obj2Error, - title: obj2.attributes.title, meta: { title: obj2.attributes.title }, error: { type: 'conflict', destinationId: 'some-object-id' }, }, { ...obj4Error, - title: obj4.attributes.title, meta: { title: obj4.attributes.title }, error: { ...obj4Error.error, type: 'unknown' }, }, diff --git a/src/core/server/saved_objects/import/lib/check_conflicts.ts b/src/core/server/saved_objects/import/lib/check_conflicts.ts index c15c4302491b4..3ab4b03e791b0 100644 --- a/src/core/server/saved_objects/import/lib/check_conflicts.ts +++ b/src/core/server/saved_objects/import/lib/check_conflicts.ts @@ -80,10 +80,10 @@ export async function checkConflicts({ importStateMap.set(`${type}:${id}`, { destinationId: uuidv4(), omitOriginId }); filteredObjects.push(object); } else if (errorObj && errorObj.statusCode !== 409) { - errors.push({ type, id, title, meta: { title }, error: { ...errorObj, type: 'unknown' } }); + errors.push({ type, id, meta: { title }, error: { ...errorObj, type: 'unknown' } }); } else if (errorObj?.statusCode === 409 && !ignoreRegularConflicts && !overwrite) { const error = { type: 'conflict' as 'conflict', ...(destinationId && { destinationId }) }; - errors.push({ type, id, title, meta: { title }, error }); + errors.push({ type, id, meta: { title }, error }); } else { filteredObjects.push(object); if (errorObj?.statusCode === 409) { diff --git a/src/core/server/saved_objects/import/lib/check_origin_conflicts.test.ts b/src/core/server/saved_objects/import/lib/check_origin_conflicts.test.ts index 12aa9f668a586..30f40e5e84c62 100644 --- a/src/core/server/saved_objects/import/lib/check_origin_conflicts.test.ts +++ b/src/core/server/saved_objects/import/lib/check_origin_conflicts.test.ts @@ -176,7 +176,6 @@ describe('#checkOriginConflicts', () => { ): SavedObjectsImportFailure => ({ type: object.type, id: object.id, - title: object.attributes.title, meta: { title: object.attributes.title }, error: { type: 'ambiguous_conflict', @@ -189,7 +188,6 @@ describe('#checkOriginConflicts', () => { ): SavedObjectsImportFailure => ({ type: object.type, id: object.id, - title: object.attributes?.title, meta: { title: object.attributes.title }, error: { type: 'conflict', diff --git a/src/core/server/saved_objects/import/lib/check_origin_conflicts.ts b/src/core/server/saved_objects/import/lib/check_origin_conflicts.ts index 46d38f7c44e5e..207eb59b126cd 100644 --- a/src/core/server/saved_objects/import/lib/check_origin_conflicts.ts +++ b/src/core/server/saved_objects/import/lib/check_origin_conflicts.ts @@ -229,7 +229,6 @@ export async function checkOriginConflicts({ errors.push({ type, id, - title, meta: { title }, error: { type: 'conflict', @@ -253,7 +252,6 @@ export async function checkOriginConflicts({ errors.push({ type, id, - title, meta: { title }, error: { type: 'ambiguous_conflict', diff --git a/src/core/server/saved_objects/import/lib/collect_saved_objects.test.ts b/src/core/server/saved_objects/import/lib/collect_saved_objects.test.ts index b401d71ffe498..c0c2d9e6bfede 100644 --- a/src/core/server/saved_objects/import/lib/collect_saved_objects.test.ts +++ b/src/core/server/saved_objects/import/lib/collect_saved_objects.test.ts @@ -157,7 +157,7 @@ describe('collectSavedObjects()', () => { const error = { type: 'unsupported_type' }; const { title } = obj1.attributes; - const errors = [{ error, type: obj1.type, id: obj1.id, title, meta: { title } }]; + const errors = [{ error, type: obj1.type, id: obj1.id, meta: { title } }]; expect(result).toEqual({ collectedObjects: [], errors, importStateMap: new Map() }); }); @@ -174,7 +174,7 @@ describe('collectSavedObjects()', () => { ]); const error = { type: 'unsupported_type' }; const { title } = obj2.attributes; - const errors = [{ error, type: obj2.type, id: obj2.id, title, meta: { title } }]; + const errors = [{ error, type: obj2.type, id: obj2.id, meta: { title } }]; expect(result).toEqual({ collectedObjects, errors, importStateMap }); }); @@ -192,7 +192,7 @@ describe('collectSavedObjects()', () => { const error = { type: 'unsupported_type' }; const { title } = obj1.attributes; - const errors = [{ error, type: obj1.type, id: obj1.id, title, meta: { title } }]; + const errors = [{ error, type: obj1.type, id: obj1.id, meta: { title } }]; expect(result).toEqual({ collectedObjects: [], errors, importStateMap: new Map() }); }); @@ -215,7 +215,7 @@ describe('collectSavedObjects()', () => { ]); const error = { type: 'unsupported_type' }; const { title } = obj1.attributes; - const errors = [{ error, type: obj1.type, id: obj1.id, title, meta: { title } }]; + const errors = [{ error, type: obj1.type, id: obj1.id, meta: { title } }]; expect(result).toEqual({ collectedObjects, errors, importStateMap }); }); }); diff --git a/src/core/server/saved_objects/import/lib/collect_saved_objects.ts b/src/core/server/saved_objects/import/lib/collect_saved_objects.ts index 209ae5ecf283e..66d75d7af5073 100644 --- a/src/core/server/saved_objects/import/lib/collect_saved_objects.ts +++ b/src/core/server/saved_objects/import/lib/collect_saved_objects.ts @@ -49,7 +49,6 @@ export async function collectSavedObjects({ errors.push({ id: obj.id, type: obj.type, - title, meta: { title }, error: { type: 'unsupported_type', diff --git a/src/core/server/saved_objects/import/lib/extract_errors.test.ts b/src/core/server/saved_objects/import/lib/extract_errors.test.ts index f3a9c2d89537d..a355094f07117 100644 --- a/src/core/server/saved_objects/import/lib/extract_errors.test.ts +++ b/src/core/server/saved_objects/import/lib/extract_errors.test.ts @@ -51,45 +51,42 @@ describe('extractErrors()', () => { ]; const result = extractErrors(savedObjects, savedObjects); expect(result).toMatchInlineSnapshot(` -Array [ - Object { - "error": Object { - "type": "conflict", - }, - "id": "2", - "meta": Object { - "title": "My Dashboard 2", - }, - "title": "My Dashboard 2", - "type": "dashboard", - }, - Object { - "error": Object { - "error": "Bad Request", - "message": "Bad Request", - "statusCode": 400, - "type": "unknown", - }, - "id": "3", - "meta": Object { - "title": "My Dashboard 3", - }, - "title": "My Dashboard 3", - "type": "dashboard", - }, - Object { - "error": Object { - "destinationId": "foo", - "type": "conflict", - }, - "id": "4", - "meta": Object { - "title": "My Dashboard 4", - }, - "title": "My Dashboard 4", - "type": "dashboard", - }, -] -`); + Array [ + Object { + "error": Object { + "type": "conflict", + }, + "id": "2", + "meta": Object { + "title": "My Dashboard 2", + }, + "type": "dashboard", + }, + Object { + "error": Object { + "error": "Bad Request", + "message": "Bad Request", + "statusCode": 400, + "type": "unknown", + }, + "id": "3", + "meta": Object { + "title": "My Dashboard 3", + }, + "type": "dashboard", + }, + Object { + "error": Object { + "destinationId": "foo", + "type": "conflict", + }, + "id": "4", + "meta": Object { + "title": "My Dashboard 4", + }, + "type": "dashboard", + }, + ] + `); }); }); diff --git a/src/core/server/saved_objects/import/lib/extract_errors.ts b/src/core/server/saved_objects/import/lib/extract_errors.ts index dbc88b01f9ae2..12d7612a4f960 100644 --- a/src/core/server/saved_objects/import/lib/extract_errors.ts +++ b/src/core/server/saved_objects/import/lib/extract_errors.ts @@ -30,7 +30,6 @@ export function extractErrors( errors.push({ id: savedObject.id, type: savedObject.type, - title, meta: { title }, error: { type: 'conflict', @@ -42,7 +41,6 @@ export function extractErrors( errors.push({ id: savedObject.id, type: savedObject.type, - title, meta: { title }, error: { ...savedObject.error, diff --git a/src/core/server/saved_objects/import/lib/validate_references.ts b/src/core/server/saved_objects/import/lib/validate_references.ts index 69e036cf77a3a..b454bf8c887a4 100644 --- a/src/core/server/saved_objects/import/lib/validate_references.ts +++ b/src/core/server/saved_objects/import/lib/validate_references.ts @@ -116,7 +116,6 @@ export async function validateReferences(params: ValidateReferencesParams) { errorMap[`${type}:${id}`] = { id, type, - title, meta: { title }, error: { type: 'missing_references', references: missingReferences }, }; diff --git a/src/core/server/saved_objects/import/resolve_import_errors.test.ts b/src/core/server/saved_objects/import/resolve_import_errors.test.ts index 5218def19852a..2a228df80f2d3 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.test.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.test.ts @@ -138,7 +138,6 @@ describe('#importSavedObjectsFromStream', () => { return { type: 'foo-type', id: uuidv4(), - title: 'some-title', meta: { title }, error: { type: 'conflict' }, }; diff --git a/src/core/server/saved_objects/import/types.ts b/src/core/server/saved_objects/import/types.ts index be1a67c931830..07ddc97db8a48 100644 --- a/src/core/server/saved_objects/import/types.ts +++ b/src/core/server/saved_objects/import/types.ts @@ -89,10 +89,6 @@ export interface SavedObjectsImportMissingReferencesError { export interface SavedObjectsImportFailure { id: string; type: string; - /** - * @deprecated Use `meta.title` instead - */ - title?: string; meta: { title?: string; icon?: string }; /** * If `overwrite` is specified, an attempt was made to overwrite an existing object. diff --git a/src/core/server/saved_objects/routes/integration_tests/import.test.ts b/src/core/server/saved_objects/routes/integration_tests/import.test.ts index d5f994a3e01ea..6f2dfe86a1525 100644 --- a/src/core/server/saved_objects/routes/integration_tests/import.test.ts +++ b/src/core/server/saved_objects/routes/integration_tests/import.test.ts @@ -229,7 +229,6 @@ describe(`POST ${URL}`, () => { { id: mockIndexPattern.id, type: mockIndexPattern.type, - title: mockIndexPattern.attributes.title, meta: { title: mockIndexPattern.attributes.title, icon: 'index-pattern-icon' }, error: { type: 'conflict' }, }, @@ -322,7 +321,6 @@ describe(`POST ${URL}`, () => { { id: 'my-vis', type: 'visualization', - title: 'my-vis', meta: { title: 'my-vis', icon: 'visualization-icon' }, error: { type: 'missing_references', @@ -386,7 +384,6 @@ describe(`POST ${URL}`, () => { { id: 'my-vis', type: 'visualization', - title: 'my-vis', meta: { title: 'my-vis', icon: 'visualization-icon' }, error: { type: 'missing_references', @@ -396,7 +393,6 @@ describe(`POST ${URL}`, () => { { id: 'my-vis', type: 'visualization', - title: 'my-vis', meta: { title: 'my-vis', icon: 'visualization-icon' }, error: { type: 'conflict' }, }, @@ -457,7 +453,6 @@ describe(`POST ${URL}`, () => { { id: 'my-vis', type: 'visualization', - title: 'my-vis', meta: { title: 'my-vis', icon: 'visualization-icon' }, overwrite: true, error: { diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 6d5b06346225b..65ef524f83949 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2501,8 +2501,6 @@ export interface SavedObjectsImportFailure { icon?: string; }; overwrite?: boolean; - // @deprecated (undocumented) - title?: string; // (undocumented) type: string; } diff --git a/test/api_integration/apis/saved_objects/import.ts b/test/api_integration/apis/saved_objects/import.ts index c899f082ec4d3..205325ae745bb 100644 --- a/test/api_integration/apis/saved_objects/import.ts +++ b/test/api_integration/apis/saved_objects/import.ts @@ -16,7 +16,6 @@ const createConflictError = ( object: Omit ): SavedObjectsImportFailure => ({ ...object, - title: object.meta.title, error: { type: 'conflict' }, }); @@ -123,7 +122,6 @@ export default function ({ getService }: FtrProviderContext) { { id: '1', type: 'wigwags', - title: 'my title', meta: { title: 'my title' }, error: { type: 'unsupported_type' }, }, @@ -221,7 +219,6 @@ export default function ({ getService }: FtrProviderContext) { { type: 'visualization', id: '1', - title: 'My visualization', meta: { title: 'My visualization', icon: 'visualizeApp' }, error: { type: 'missing_references', diff --git a/test/plugin_functional/test_suites/saved_objects_hidden_type/import.ts b/test/plugin_functional/test_suites/saved_objects_hidden_type/import.ts index 70241ade50a3d..f8fa3c11a1b8c 100644 --- a/test/plugin_functional/test_suites/saved_objects_hidden_type/import.ts +++ b/test/plugin_functional/test_suites/saved_objects_hidden_type/import.ts @@ -72,7 +72,6 @@ export default function ({ getService }: PluginFunctionalProviderContext) { { id: 'some-id-1', type: 'test-hidden-non-importable-exportable', - title: 'my title', meta: { title: 'my title', }, diff --git a/test/plugin_functional/test_suites/saved_objects_hidden_type/resolve_import_errors.ts b/test/plugin_functional/test_suites/saved_objects_hidden_type/resolve_import_errors.ts index 8af5e07ab69ad..be2af8cd5d115 100644 --- a/test/plugin_functional/test_suites/saved_objects_hidden_type/resolve_import_errors.ts +++ b/test/plugin_functional/test_suites/saved_objects_hidden_type/resolve_import_errors.ts @@ -95,7 +95,6 @@ export default function ({ getService }: PluginFunctionalProviderContext) { { id: 'op3767a1-9rcg-53u7-jkb3-3dnb74193awc', type: 'test-hidden-non-importable-exportable', - title: 'new title!', meta: { title: 'new title!', }, diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts index 8ade75e90b541..bd343cad737aa 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts @@ -416,7 +416,6 @@ export function copyToSpaceTestSuiteFactory( destinationId: `cts_dashboard_${destination}`, // this conflicted with another dashboard in the destination space because of a shared originId }, id: `cts_dashboard_${spaceId}`, - title: `This is the ${spaceId} test space CTS dashboard`, type: 'dashboard', meta: { title: `This is the ${spaceId} test space CTS dashboard`, @@ -429,7 +428,6 @@ export function copyToSpaceTestSuiteFactory( destinationId: `cts_ip_1_${destination}`, // this conflicted with another index pattern in the destination space because of a shared originId }, id: `cts_ip_1_${spaceId}`, - title: `Copy to Space index pattern 1 from ${spaceId} space`, type: 'index-pattern', meta: { title: `Copy to Space index pattern 1 from ${spaceId} space`, @@ -442,7 +440,6 @@ export function copyToSpaceTestSuiteFactory( destinationId: `cts_vis_3_${destination}`, // this conflicted with another visualization in the destination space because of a shared originId }, id: `cts_vis_3_${spaceId}`, - title: `CTS vis 3 from ${spaceId} space`, type: 'visualization', meta: { title: `CTS vis 3 from ${spaceId} space`, @@ -591,7 +588,6 @@ export function copyToSpaceTestSuiteFactory( error: { type: 'conflict', destinationId }, type, id: inexactMatchIdA, - title, meta, }, ]); @@ -633,7 +629,6 @@ export function copyToSpaceTestSuiteFactory( error: { type: 'conflict', destinationId }, type, id: inexactMatchIdB, - title, meta, }, ]); @@ -675,7 +670,6 @@ export function copyToSpaceTestSuiteFactory( error: { type: 'conflict', destinationId }, type, id: inexactMatchIdC, - title, meta, }, ]); @@ -721,7 +715,6 @@ export function copyToSpaceTestSuiteFactory( error: { type: 'ambiguous_conflict', destinations }, type, id: ambiguousConflictId, - title, meta: { title, icon: 'beaker' }, }, ]); diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts index 336b04832e2dc..75aea0a3fe4e0 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts @@ -163,7 +163,6 @@ export function resolveCopyToSpaceConflictsSuite( destinationId: `cts_ip_1_${destination}`, // this conflicted with another index pattern in the destination space because of a shared originId }, id: `cts_ip_1_${sourceSpaceId}`, - title: `Copy to Space index pattern 1 from ${sourceSpaceId} space`, meta: { title: `Copy to Space index pattern 1 from ${sourceSpaceId} space`, icon: 'indexPatternApp', @@ -176,7 +175,6 @@ export function resolveCopyToSpaceConflictsSuite( destinationId: `cts_vis_3_${destination}`, // this conflicted with another visualization in the destination space because of a shared originId }, id: `cts_vis_3_${sourceSpaceId}`, - title: `CTS vis 3 from ${sourceSpaceId} space`, meta: { title: `CTS vis 3 from ${sourceSpaceId} space`, icon: 'visualizeApp', @@ -211,7 +209,6 @@ export function resolveCopyToSpaceConflictsSuite( }, id: `cts_dashboard_${sourceSpaceId}`, type: 'dashboard', - title: `This is the ${sourceSpaceId} test space CTS dashboard`, meta: { title: `This is the ${sourceSpaceId} test space CTS dashboard`, icon: 'dashboardApp', From 22369fd874cec1c0e23eff94fa6ff6fe2a49c9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Wed, 9 Mar 2022 08:09:26 +0100 Subject: [PATCH 091/140] Remove deprecated & unused `public-AsyncPlugin` (#127048) --- .../kibana-plugin-core-public.asyncplugin.md | 27 --- ...na-plugin-core-public.asyncplugin.setup.md | 23 --- ...na-plugin-core-public.asyncplugin.start.md | 23 --- ...ana-plugin-core-public.asyncplugin.stop.md | 15 -- .../core/public/kibana-plugin-core-public.md | 1 - ...na-plugin-core-public.plugininitializer.md | 2 +- src/core/public/index.ts | 9 +- src/core/public/plugins/index.ts | 2 +- src/core/public/plugins/plugin.test.ts | 4 +- src/core/public/plugins/plugin.ts | 44 +---- .../plugins/plugins_service.test.mocks.ts | 7 +- .../public/plugins/plugins_service.test.ts | 178 ------------------ src/core/public/plugins/plugins_service.ts | 54 +----- src/core/public/public.api.md | 12 +- 14 files changed, 14 insertions(+), 387 deletions(-) delete mode 100644 docs/development/core/public/kibana-plugin-core-public.asyncplugin.md delete mode 100644 docs/development/core/public/kibana-plugin-core-public.asyncplugin.setup.md delete mode 100644 docs/development/core/public/kibana-plugin-core-public.asyncplugin.start.md delete mode 100644 docs/development/core/public/kibana-plugin-core-public.asyncplugin.stop.md diff --git a/docs/development/core/public/kibana-plugin-core-public.asyncplugin.md b/docs/development/core/public/kibana-plugin-core-public.asyncplugin.md deleted file mode 100644 index cb9559dddc684..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.asyncplugin.md +++ /dev/null @@ -1,27 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) - -## AsyncPlugin interface - -> Warning: This API is now obsolete. -> -> Asynchronous lifecycles are deprecated, and should be migrated to sync -> - -A plugin with asynchronous lifecycle methods. - -Signature: - -```typescript -export interface AsyncPlugin -``` - -## Methods - -| Method | Description | -| --- | --- | -| [setup(core, plugins)](./kibana-plugin-core-public.asyncplugin.setup.md) | | -| [start(core, plugins)](./kibana-plugin-core-public.asyncplugin.start.md) | | -| [stop()?](./kibana-plugin-core-public.asyncplugin.stop.md) | (Optional) | - diff --git a/docs/development/core/public/kibana-plugin-core-public.asyncplugin.setup.md b/docs/development/core/public/kibana-plugin-core-public.asyncplugin.setup.md deleted file mode 100644 index 67a5dad22a0a2..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.asyncplugin.setup.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) > [setup](./kibana-plugin-core-public.asyncplugin.setup.md) - -## AsyncPlugin.setup() method - -Signature: - -```typescript -setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| core | CoreSetup<TPluginsStart, TStart> | | -| plugins | TPluginsSetup | | - -Returns: - -TSetup \| Promise<TSetup> - diff --git a/docs/development/core/public/kibana-plugin-core-public.asyncplugin.start.md b/docs/development/core/public/kibana-plugin-core-public.asyncplugin.start.md deleted file mode 100644 index 89554a1afaf1a..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.asyncplugin.start.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) > [start](./kibana-plugin-core-public.asyncplugin.start.md) - -## AsyncPlugin.start() method - -Signature: - -```typescript -start(core: CoreStart, plugins: TPluginsStart): TStart | Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| core | CoreStart | | -| plugins | TPluginsStart | | - -Returns: - -TStart \| Promise<TStart> - diff --git a/docs/development/core/public/kibana-plugin-core-public.asyncplugin.stop.md b/docs/development/core/public/kibana-plugin-core-public.asyncplugin.stop.md deleted file mode 100644 index 3fb7504879cf6..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.asyncplugin.stop.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) > [stop](./kibana-plugin-core-public.asyncplugin.stop.md) - -## AsyncPlugin.stop() method - -Signature: - -```typescript -stop?(): void; -``` -Returns: - -void - diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index fca697144a872..2e51a036dfe9f 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -39,7 +39,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ApplicationStart](./kibana-plugin-core-public.applicationstart.md) | | | [AppMountParameters](./kibana-plugin-core-public.appmountparameters.md) | | | [AppNavOptions](./kibana-plugin-core-public.appnavoptions.md) | App navigation menu options | -| [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) | A plugin with asynchronous lifecycle methods. | | [Capabilities](./kibana-plugin-core-public.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. | | [ChromeBadge](./kibana-plugin-core-public.chromebadge.md) | | | [ChromeDocTitle](./kibana-plugin-core-public.chromedoctitle.md) | APIs for accessing and updating the document title. | diff --git a/docs/development/core/public/kibana-plugin-core-public.plugininitializer.md b/docs/development/core/public/kibana-plugin-core-public.plugininitializer.md index b7c3e11e492bd..1fcc2999dfd2e 100644 --- a/docs/development/core/public/kibana-plugin-core-public.plugininitializer.md +++ b/docs/development/core/public/kibana-plugin-core-public.plugininitializer.md @@ -9,5 +9,5 @@ The `plugin` export at the root of a plugin's `public` directory should conform Signature: ```typescript -export declare type PluginInitializer = (core: PluginInitializerContext) => Plugin | AsyncPlugin; +export declare type PluginInitializer = (core: PluginInitializerContext) => Plugin; ``` diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 50d8bf304ddf8..902f93d108aaf 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -52,13 +52,7 @@ import { HttpSetup, HttpStart } from './http'; import { I18nStart } from './i18n'; import { NotificationsSetup, NotificationsStart } from './notifications'; import { OverlayStart } from './overlays'; -import { - Plugin, - AsyncPlugin, - PluginInitializer, - PluginInitializerContext, - PluginOpaqueId, -} from './plugins'; +import { Plugin, PluginInitializer, PluginInitializerContext, PluginOpaqueId } from './plugins'; import { UiSettingsState, IUiSettingsClient } from './ui_settings'; import { ApplicationSetup, Capabilities, ApplicationStart } from './application'; import { DocLinksStart } from './doc_links'; @@ -332,7 +326,6 @@ export type { NotificationsSetup, NotificationsStart, Plugin, - AsyncPlugin, PluginInitializer, PluginInitializerContext, SavedObjectsStart, diff --git a/src/core/public/plugins/index.ts b/src/core/public/plugins/index.ts index 9d7a61ef47a0d..976ec660ac9bf 100644 --- a/src/core/public/plugins/index.ts +++ b/src/core/public/plugins/index.ts @@ -7,6 +7,6 @@ */ export { PluginsService } from './plugins_service'; -export type { Plugin, AsyncPlugin, PluginInitializer } from './plugin'; +export type { Plugin, PluginInitializer } from './plugin'; export type { PluginInitializerContext } from './plugin_context'; export type { PluginOpaqueId } from '../../server/types'; diff --git a/src/core/public/plugins/plugin.test.ts b/src/core/public/plugins/plugin.test.ts index 8deef6ac9f727..2c3bfc6961fb5 100644 --- a/src/core/public/plugins/plugin.test.ts +++ b/src/core/public/plugins/plugin.test.ts @@ -94,9 +94,7 @@ describe('PluginWrapper', () => { mockPluginReader.mockReturnValueOnce( jest.fn(() => ({ setup: jest.fn(), - start: jest.fn(async () => { - // Add small delay to ensure startDependencies is not resolved until after the plugin instance's start resolves. - await new Promise((resolve) => setTimeout(resolve, 10)); + start: jest.fn(() => { expect(startDependenciesResolved).toBe(false); return pluginStartContract; }), diff --git a/src/core/public/plugins/plugin.ts b/src/core/public/plugins/plugin.ts index a08a6cf0b431a..a43cc2046b82d 100644 --- a/src/core/public/plugins/plugin.ts +++ b/src/core/public/plugins/plugin.ts @@ -8,7 +8,6 @@ import { Subject } from 'rxjs'; import { first } from 'rxjs/operators'; -import { isPromise } from '@kbn/std'; import { DiscoveredPlugin, PluginOpaqueId } from '../../server'; import { PluginInitializerContext } from './plugin_context'; import { read } from './plugin_reader'; @@ -30,23 +29,6 @@ export interface Plugin< stop?(): void; } -/** - * A plugin with asynchronous lifecycle methods. - * - * @deprecated Asynchronous lifecycles are deprecated, and should be migrated to sync {@link Plugin | plugin} - * @public - */ -export interface AsyncPlugin< - TSetup = void, - TStart = void, - TPluginsSetup extends object = object, - TPluginsStart extends object = object -> { - setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; - start(core: CoreStart, plugins: TPluginsStart): TStart | Promise; - stop?(): void; -} - /** * The `plugin` export at the root of a plugin's `public` directory should conform * to this interface. @@ -58,11 +40,7 @@ export type PluginInitializer< TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object -> = ( - core: PluginInitializerContext -) => - | Plugin - | AsyncPlugin; +> = (core: PluginInitializerContext) => Plugin; /** * Lightweight wrapper around discovered plugin that is responsible for instantiating @@ -80,9 +58,7 @@ export class PluginWrapper< public readonly configPath: DiscoveredPlugin['configPath']; public readonly requiredPlugins: DiscoveredPlugin['requiredPlugins']; public readonly optionalPlugins: DiscoveredPlugin['optionalPlugins']; - private instance?: - | Plugin - | AsyncPlugin; + private instance?: Plugin; private readonly startDependencies$ = new Subject<[CoreStart, TPluginsStart, TStart]>(); public readonly startDependencies = this.startDependencies$.pipe(first()).toPromise(); @@ -105,10 +81,7 @@ export class PluginWrapper< * @param plugins The dictionary where the key is the dependency name and the value * is the contract returned by the dependency's `setup` function. */ - public setup( - setupContext: CoreSetup, - plugins: TPluginsSetup - ): TSetup | Promise { + public setup(setupContext: CoreSetup, plugins: TPluginsSetup): TSetup { this.instance = this.createPluginInstance(); return this.instance.setup(setupContext, plugins); } @@ -126,15 +99,8 @@ export class PluginWrapper< } const startContract = this.instance.start(startContext, plugins); - if (isPromise(startContract)) { - return startContract.then((resolvedContract) => { - this.startDependencies$.next([startContext, plugins, resolvedContract]); - return resolvedContract; - }); - } else { - this.startDependencies$.next([startContext, plugins, startContract]); - return startContract; - } + this.startDependencies$.next([startContext, plugins, startContract]); + return startContract; } /** diff --git a/src/core/public/plugins/plugins_service.test.mocks.ts b/src/core/public/plugins/plugins_service.test.mocks.ts index 1858008e28016..801ea20015f2a 100644 --- a/src/core/public/plugins/plugins_service.test.mocks.ts +++ b/src/core/public/plugins/plugins_service.test.mocks.ts @@ -7,12 +7,9 @@ */ import { PluginName } from 'kibana/server'; -import { Plugin, AsyncPlugin } from './plugin'; +import { Plugin } from './plugin'; -export type MockedPluginInitializer = jest.Mock< - Plugin | AsyncPlugin, - any ->; +export type MockedPluginInitializer = jest.Mock>; export const mockPluginInitializerProvider: jest.Mock = jest .fn() diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index 40976424b7edd..390cba409257f 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -251,36 +251,6 @@ describe('PluginsService', () => { expect((contracts.get('pluginA')! as any).setupValue).toEqual(1); expect((contracts.get('pluginB')! as any).pluginAPlusB).toEqual(2); }); - - describe('timeout', () => { - const flushPromises = () => new Promise((resolve) => setImmediate(resolve)); - beforeAll(() => { - jest.useFakeTimers(); - }); - afterAll(() => { - jest.useRealTimers(); - }); - - it('throws timeout error if "setup" was not completed in 30 sec.', async () => { - mockPluginInitializers.set( - 'pluginA', - jest.fn(() => ({ - setup: jest.fn(() => new Promise((i) => i)), - start: jest.fn(() => ({ value: 1 })), - stop: jest.fn(), - })) - ); - const pluginsService = new PluginsService(mockCoreContext, plugins); - const promise = pluginsService.setup(mockSetupDeps); - - await flushPromises(); - jest.runAllTimers(); // setup plugins - - await expect(promise).rejects.toMatchInlineSnapshot( - `[Error: Setup lifecycle of "pluginA" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.]` - ); - }); - }); }); describe('#start()', () => { @@ -333,34 +303,6 @@ describe('PluginsService', () => { expect((contracts.get('pluginA')! as any).startValue).toEqual(2); expect((contracts.get('pluginB')! as any).pluginAPlusB).toEqual(3); }); - describe('timeout', () => { - beforeAll(() => { - jest.useFakeTimers(); - }); - afterAll(() => { - jest.useRealTimers(); - }); - - it('throws timeout error if "start" was not completed in 30 sec.', async () => { - mockPluginInitializers.set( - 'pluginA', - jest.fn(() => ({ - setup: jest.fn(() => ({ value: 1 })), - start: jest.fn(() => new Promise((i) => i)), - stop: jest.fn(), - })) - ); - const pluginsService = new PluginsService(mockCoreContext, plugins); - await pluginsService.setup(mockSetupDeps); - - const promise = pluginsService.start(mockStartDeps); - jest.runAllTimers(); - - await expect(promise).rejects.toMatchInlineSnapshot( - `[Error: Start lifecycle of "pluginA" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.]` - ); - }); - }); }); describe('#stop()', () => { @@ -379,124 +321,4 @@ describe('PluginsService', () => { expect(pluginCInstance.stop).toHaveBeenCalled(); }); }); - - describe('asynchronous plugins', () => { - let consoleSpy: jest.SpyInstance; - - beforeEach(() => { - consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined); - }); - - afterEach(() => { - consoleSpy.mockRestore(); - }); - - const runScenario = async ({ - production, - asyncSetup, - asyncStart, - }: { - production: boolean; - asyncSetup: boolean; - asyncStart: boolean; - }) => { - const coreContext = coreMock.createCoreContext({ production }); - - const syncPlugin = { id: 'sync-plugin', plugin: createManifest('sync-plugin') }; - mockPluginInitializers.set( - 'sync-plugin', - jest.fn(() => ({ - setup: jest.fn(() => 'setup-sync'), - start: jest.fn(() => 'start-sync'), - stop: jest.fn(), - })) - ); - - const asyncPlugin = { id: 'async-plugin', plugin: createManifest('async-plugin') }; - mockPluginInitializers.set( - 'async-plugin', - jest.fn(() => ({ - setup: jest.fn(() => (asyncSetup ? Promise.resolve('setup-async') : 'setup-sync')), - start: jest.fn(() => (asyncStart ? Promise.resolve('start-async') : 'start-sync')), - stop: jest.fn(), - })) - ); - - const pluginsService = new PluginsService(coreContext, [syncPlugin, asyncPlugin]); - - await pluginsService.setup(mockSetupDeps); - await pluginsService.start(mockStartDeps); - }; - - it('logs a warning if a plugin returns a promise from its setup contract in dev mode', async () => { - await runScenario({ - production: false, - asyncSetup: true, - asyncStart: false, - }); - - expect(consoleSpy.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - "Plugin async-plugin is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.", - ], - ] - `); - }); - - it('does not log warnings if a plugin returns a promise from its setup contract in prod mode', async () => { - await runScenario({ - production: true, - asyncSetup: true, - asyncStart: false, - }); - - expect(consoleSpy).not.toHaveBeenCalled(); - }); - - it('logs a warning if a plugin returns a promise from its start contract in dev mode', async () => { - await runScenario({ - production: false, - asyncSetup: false, - asyncStart: true, - }); - - expect(consoleSpy.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - "Plugin async-plugin is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.", - ], - ] - `); - }); - - it('does not log warnings if a plugin returns a promise from its start contract in prod mode', async () => { - await runScenario({ - production: true, - asyncSetup: false, - asyncStart: true, - }); - - expect(consoleSpy).not.toHaveBeenCalled(); - }); - - it('logs multiple warnings if both `setup` and `start` return promises', async () => { - await runScenario({ - production: false, - asyncSetup: true, - asyncStart: true, - }); - - expect(consoleSpy.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - "Plugin async-plugin is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.", - ], - Array [ - "Plugin async-plugin is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.", - ], - ] - `); - }); - }); }); diff --git a/src/core/public/plugins/plugins_service.ts b/src/core/public/plugins/plugins_service.ts index 230a675b4cda6..51af5a831d44b 100644 --- a/src/core/public/plugins/plugins_service.ts +++ b/src/core/public/plugins/plugins_service.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { withTimeout, isPromise } from '@kbn/std'; import { PluginName, PluginOpaqueId } from '../../server'; import { CoreService } from '../../types'; import { CoreContext } from '../core_system'; @@ -19,7 +18,6 @@ import { import { InternalCoreSetup, InternalCoreStart } from '../core_system'; import { InjectedPluginMetadata } from '../injected_metadata'; -const Sec = 1000; /** @internal */ export type PluginsServiceSetupDeps = InternalCoreSetup; /** @internal */ @@ -98,34 +96,10 @@ export class PluginsService implements CoreService ); - let contract: unknown; - const contractOrPromise = plugin.setup( + const contract = plugin.setup( createPluginSetupContext(this.coreContext, deps, plugin), pluginDepContracts ); - if (isPromise(contractOrPromise)) { - if (this.coreContext.env.mode.dev) { - // eslint-disable-next-line no-console - console.log( - `Plugin ${pluginName} is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.` - ); - } - - const contractMaybe = await withTimeout({ - promise: contractOrPromise, - timeoutMs: 10 * Sec, - }); - - if (contractMaybe.timedout) { - throw new Error( - `Setup lifecycle of "${pluginName}" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.` - ); - } else { - contract = contractMaybe.value; - } - } else { - contract = contractOrPromise; - } contracts.set(pluginName, contract); this.satupPlugins.push(pluginName); @@ -152,34 +126,10 @@ export class PluginsService implements CoreService ); - let contract: unknown; - const contractOrPromise = plugin.start( + const contract = plugin.start( createPluginStartContext(this.coreContext, deps, plugin), pluginDepContracts ); - if (isPromise(contractOrPromise)) { - if (this.coreContext.env.mode.dev) { - // eslint-disable-next-line no-console - console.log( - `Plugin ${pluginName} is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.` - ); - } - - const contractMaybe = await withTimeout({ - promise: contractOrPromise, - timeoutMs: 10 * Sec, - }); - - if (contractMaybe.timedout) { - throw new Error( - `Start lifecycle of "${pluginName}" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.` - ); - } else { - contract = contractMaybe.value; - } - } else { - contract = contractOrPromise; - } contracts.set(pluginName, contract); } diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 348d20ea02e52..ed119620ac08b 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -209,16 +209,6 @@ export type AppUpdatableFields = Pick Partial | undefined; -// @public @deprecated -export interface AsyncPlugin { - // (undocumented) - setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; - // (undocumented) - start(core: CoreStart, plugins: TPluginsStart): TStart | Promise; - // (undocumented) - stop?(): void; -} - // @public export interface Capabilities { [key: string]: Record>; @@ -920,7 +910,7 @@ interface Plugin_2 = (core: PluginInitializerContext) => Plugin_2 | AsyncPlugin; +export type PluginInitializer = (core: PluginInitializerContext) => Plugin_2; // @public export interface PluginInitializerContext { From b76370d830c48b510f0fbfac383284f3155d3ef5 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Wed, 9 Mar 2022 02:24:54 -0500 Subject: [PATCH 092/140] Bump dependencies (#127238) --- package.json | 5 +- packages/kbn-pm/dist/index.js | 162 +++++++++++++++++++--------------- yarn.lock | 20 ++--- 3 files changed, 102 insertions(+), 85 deletions(-) diff --git a/package.json b/package.json index 55a33716e7614..ebae482109fd5 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "**/isomorphic-fetch/node-fetch": "^2.6.7", "**/istanbul-lib-coverage": "^3.2.0", "**/json-schema": "^0.4.0", + "**/minimatch": "^3.1.2", "**/minimist": "^1.2.5", "**/node-forge": "^1.2.1", "**/pdfkit/crypto-js": "4.0.0", @@ -277,7 +278,7 @@ "js-search": "^1.4.3", "js-sha256": "^0.9.0", "js-sql-parser": "^1.4.1", - "js-yaml": "^3.14.0", + "js-yaml": "^3.14.1", "json-stable-stringify": "^1.0.1", "json-stringify-pretty-compact": "1.2.0", "json-stringify-safe": "5.0.1", @@ -298,7 +299,7 @@ "mime": "^2.4.4", "mime-types": "^2.1.27", "mini-css-extract-plugin": "1.1.0", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "moment": "^2.24.0", "moment-duration-format": "^2.3.2", "moment-timezone": "^0.5.27", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 00f13d6695afd..6c64d0bd2ca00 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -19783,10 +19783,10 @@ exports.realpath = function realpath(p, cache, cb) { module.exports = minimatch minimatch.Minimatch = Minimatch -var path = { sep: '/' } -try { - path = __webpack_require__(4) -} catch (er) {} +var path = (function () { try { return __webpack_require__(4) } catch (e) {}}()) || { + sep: '/' +} +minimatch.sep = path.sep var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} var expand = __webpack_require__(248) @@ -19838,43 +19838,64 @@ function filter (pattern, options) { } function ext (a, b) { - a = a || {} b = b || {} var t = {} - Object.keys(b).forEach(function (k) { - t[k] = b[k] - }) Object.keys(a).forEach(function (k) { t[k] = a[k] }) + Object.keys(b).forEach(function (k) { + t[k] = b[k] + }) return t } minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return minimatch + if (!def || typeof def !== 'object' || !Object.keys(def).length) { + return minimatch + } var orig = minimatch var m = function minimatch (p, pattern, options) { - return orig.minimatch(p, pattern, ext(def, options)) + return orig(p, pattern, ext(def, options)) } m.Minimatch = function Minimatch (pattern, options) { return new orig.Minimatch(pattern, ext(def, options)) } + m.Minimatch.defaults = function defaults (options) { + return orig.defaults(ext(def, options)).Minimatch + } + + m.filter = function filter (pattern, options) { + return orig.filter(pattern, ext(def, options)) + } + + m.defaults = function defaults (options) { + return orig.defaults(ext(def, options)) + } + + m.makeRe = function makeRe (pattern, options) { + return orig.makeRe(pattern, ext(def, options)) + } + + m.braceExpand = function braceExpand (pattern, options) { + return orig.braceExpand(pattern, ext(def, options)) + } + + m.match = function (list, pattern, options) { + return orig.match(list, pattern, ext(def, options)) + } return m } Minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return Minimatch return minimatch.defaults(def).Minimatch } function minimatch (p, pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('glob pattern string required') - } + assertValidPattern(pattern) if (!options) options = {} @@ -19883,9 +19904,6 @@ function minimatch (p, pattern, options) { return false } - // "" only matches "" - if (pattern.trim() === '') return p === '' - return new Minimatch(pattern, options).match(p) } @@ -19894,15 +19912,14 @@ function Minimatch (pattern, options) { return new Minimatch(pattern, options) } - if (typeof pattern !== 'string') { - throw new TypeError('glob pattern string required') - } + assertValidPattern(pattern) if (!options) options = {} + pattern = pattern.trim() // windows support: need to use /, not \ - if (path.sep !== '/') { + if (!options.allowWindowsEscape && path.sep !== '/') { pattern = pattern.split(path.sep).join('/') } @@ -19913,6 +19930,7 @@ function Minimatch (pattern, options) { this.negate = false this.comment = false this.empty = false + this.partial = !!options.partial // make the set of regexps etc. this.make() @@ -19922,9 +19940,6 @@ Minimatch.prototype.debug = function () {} Minimatch.prototype.make = make function make () { - // don't do it more than once. - if (this._made) return - var pattern = this.pattern var options = this.options @@ -19944,7 +19959,7 @@ function make () { // step 2: expand braces var set = this.globSet = this.braceExpand() - if (options.debug) this.debug = console.error + if (options.debug) this.debug = function debug() { console.error.apply(console, arguments) } this.debug(this.pattern, set) @@ -20024,12 +20039,11 @@ function braceExpand (pattern, options) { pattern = typeof pattern === 'undefined' ? this.pattern : pattern - if (typeof pattern === 'undefined') { - throw new TypeError('undefined pattern') - } + assertValidPattern(pattern) - if (options.nobrace || - !pattern.match(/\{.*\}/)) { + // Thanks to Yeting Li for + // improving this regexp to avoid a ReDOS vulnerability. + if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { // shortcut. no need to expand. return [pattern] } @@ -20037,6 +20051,17 @@ function braceExpand (pattern, options) { return expand(pattern) } +var MAX_PATTERN_LENGTH = 1024 * 64 +var assertValidPattern = function (pattern) { + if (typeof pattern !== 'string') { + throw new TypeError('invalid pattern') + } + + if (pattern.length > MAX_PATTERN_LENGTH) { + throw new TypeError('pattern is too long') + } +} + // parse a component of the expanded set. // At this point, no pattern may contain "/" in it // so we're going to return a 2d array, where each entry is the full @@ -20051,14 +20076,17 @@ function braceExpand (pattern, options) { Minimatch.prototype.parse = parse var SUBPARSE = {} function parse (pattern, isSub) { - if (pattern.length > 1024 * 64) { - throw new TypeError('pattern is too long') - } + assertValidPattern(pattern) var options = this.options // shortcuts - if (!options.noglobstar && pattern === '**') return GLOBSTAR + if (pattern === '**') { + if (!options.noglobstar) + return GLOBSTAR + else + pattern = '*' + } if (pattern === '') return '' var re = '' @@ -20114,10 +20142,12 @@ function parse (pattern, isSub) { } switch (c) { - case '/': + /* istanbul ignore next */ + case '/': { // completely not allowed, even escaped. // Should already be path-split by now. return false + } case '\\': clearStateChar() @@ -20236,25 +20266,23 @@ function parse (pattern, isSub) { // handle the case where we left a class open. // "[z-a]" is valid, equivalent to "\[z-a\]" - if (inClass) { - // split where the last [ was, make sure we don't have - // an invalid re. if so, re-walk the contents of the - // would-be class to re-translate any characters that - // were passed through as-is - // TODO: It would probably be faster to determine this - // without a try/catch and a new RegExp, but it's tricky - // to do safely. For now, this is safe and works. - var cs = pattern.substring(classStart + 1, i) - try { - RegExp('[' + cs + ']') - } catch (er) { - // not a valid class! - var sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' - hasMagic = hasMagic || sp[1] - inClass = false - continue - } + // split where the last [ was, make sure we don't have + // an invalid re. if so, re-walk the contents of the + // would-be class to re-translate any characters that + // were passed through as-is + // TODO: It would probably be faster to determine this + // without a try/catch and a new RegExp, but it's tricky + // to do safely. For now, this is safe and works. + var cs = pattern.substring(classStart + 1, i) + try { + RegExp('[' + cs + ']') + } catch (er) { + // not a valid class! + var sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' + hasMagic = hasMagic || sp[1] + inClass = false + continue } // finish up the class. @@ -20338,9 +20366,7 @@ function parse (pattern, isSub) { // something that could conceivably capture a dot var addPatternStart = false switch (re.charAt(0)) { - case '.': - case '[': - case '(': addPatternStart = true + case '[': case '.': case '(': addPatternStart = true } // Hack to work around lack of negative lookbehind in JS @@ -20402,7 +20428,7 @@ function parse (pattern, isSub) { var flags = options.nocase ? 'i' : '' try { var regExp = new RegExp('^' + re + '$', flags) - } catch (er) { + } catch (er) /* istanbul ignore next - should be impossible */ { // If it was an invalid regular expression, then it can't match // anything. This trick looks for a character after the end of // the string, which is of course impossible, except in multi-line @@ -20460,7 +20486,7 @@ function makeRe () { try { this.regexp = new RegExp(re, flags) - } catch (ex) { + } catch (ex) /* istanbul ignore next - should be impossible */ { this.regexp = false } return this.regexp @@ -20478,8 +20504,8 @@ minimatch.match = function (list, pattern, options) { return list } -Minimatch.prototype.match = match -function match (f, partial) { +Minimatch.prototype.match = function match (f, partial) { + if (typeof partial === 'undefined') partial = this.partial this.debug('match', f, this.pattern) // short-circuit in the case of busted things. // comments, etc. @@ -20561,6 +20587,7 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) { // should be impossible. // some invalid regexp stuff in the set. + /* istanbul ignore if */ if (p === false) return false if (p === GLOBSTAR) { @@ -20634,6 +20661,7 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) { // no match was found. // However, in partial mode, we can't say this is necessarily over. // If there's more *pattern* left, then + /* istanbul ignore if */ if (partial) { // ran out of file this.debug('\n>>> no match, partial?', file, fr, pattern, pr) @@ -20647,11 +20675,7 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) { // patterns with magic have been turned into regexps. var hit if (typeof p === 'string') { - if (options.nocase) { - hit = f.toLowerCase() === p.toLowerCase() - } else { - hit = f === p - } + hit = f === p this.debug('string match', p, f, hit) } else { hit = f.match(p) @@ -20682,16 +20706,16 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) { // this is ok if we're doing the match as part of // a glob fs traversal. return partial - } else if (pi === pl) { + } else /* istanbul ignore else */ if (pi === pl) { // ran out of pattern, still have file left. // this is only acceptable if we're on the very last // empty segment of a file with a trailing slash. // a/* should match a/b/ - var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') - return emptyFileEnd + return (fi === fl - 1) && (file[fi] === '') } // should be unreachable. + /* istanbul ignore next */ throw new Error('wtf?') } diff --git a/yarn.lock b/yarn.lock index 8c2b1f60503d8..f23e108834c79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18241,7 +18241,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1: +js-yaml@3.14.1, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.9.0: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -18256,7 +18256,7 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js-yaml@^3.13.1, js-yaml@^3.9.0, js-yaml@~3.13.1: +js-yaml@~3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -18264,14 +18264,6 @@ js-yaml@^3.13.1, js-yaml@^3.9.0, js-yaml@~3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -20023,10 +20015,10 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.2, minimatch@~3.0.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" From 133e57ffb91e983f4412f6b9621bad2b4c33d17e Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 9 Mar 2022 09:31:29 +0100 Subject: [PATCH 093/140] [Lens] Show underlying data editor navigation (#125983) * :alembic: First steps * :tada: Initial implementation for button insider editor * :white_check_mark: fix types and tests * :white_check_mark: Add some tests and some test placeholders * :bug: Fix issues on mount * :fire: Remove unused attr * :white_check_mark: Add more tests for edge cases * :recycle: First code refactor * :bug: Fix discover capabilities check * :bug: Fix various issues * :white_check_mark: Add functional tests * :white_check_mark: Add more tests * :sparkles: Add support for terms + multiterms * :sparkles: Make link open a new window with discover * :white_check_make: Update functional tests to deal with new tab * :bug: Fix transposed table case: make it use fallback * :bug: Fix unit tests * :ok_hand: Address review feedback * :bug: Skip filtered metrics if there's at least an unfiltered one * :bug: Improve string escaping strategy * :bug: Fix functional tests and improved filters dedup checks * :white_check_mark: Fix functional tests for formula case * :wrench: Make close editor panel more robust * :wrench: Try focus approach * :ok_hand: Rename variable * :white_check_mark: Try to click outside * :white_check_mark: Use the keyboard arrow approach * :ok_hand: Address some issues raised by review * :white_check_mark: Fix tests and add one more for unsupported datasource Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/top_nav_menu/top_nav_menu_data.tsx | 2 + .../public/top_nav_menu/top_nav_menu_item.tsx | 9 +- test/functional/services/filter_bar.ts | 5 + x-pack/plugins/lens/kibana.json | 3 +- .../lens/public/app_plugin/lens_top_nav.tsx | 78 ++ .../lens/public/app_plugin/mounter.tsx | 8 +- .../app_plugin/show_underlying_data.test.ts | 602 +++++++++++++ .../public/app_plugin/show_underlying_data.ts | 174 ++++ .../plugins/lens/public/app_plugin/types.ts | 4 + .../components/dimension_editor.test.tsx | 8 +- .../visualization.test.tsx | 28 +- .../config_panel/dimension_container.tsx | 8 +- .../editor_frame/editor_frame.test.tsx | 2 + .../editor_frame/suggestion_helpers.test.ts | 6 +- .../workspace_panel/chart_switch.test.tsx | 6 +- .../visualization.test.ts | 10 +- .../dimension_panel/filtering.tsx | 2 +- .../indexpattern.test.ts | 852 +++++++++++++++++- .../indexpattern_datasource/indexpattern.tsx | 50 +- .../indexpattern_suggestions.test.tsx | 25 + .../operations/definitions/terms/index.tsx | 1 + .../operations/layer_helpers.test.ts | 160 ++++ .../operations/layer_helpers.ts | 30 + .../indexpattern_datasource/query_input.tsx | 4 +- .../public/indexpattern_datasource/utils.tsx | 202 ++++- .../visualization.test.ts | 2 + .../lens/public/mocks/datasource_mock.ts | 2 + .../public/pie_visualization/to_expression.ts | 5 +- x-pack/plugins/lens/public/plugin.ts | 32 +- x-pack/plugins/lens/public/types.ts | 24 +- .../gauge/visualization.test.ts | 8 +- .../xy_visualization/to_expression.test.ts | 12 +- .../xy_visualization/visualization.test.ts | 66 +- x-pack/test/functional/apps/lens/formula.ts | 1 - x-pack/test/functional/apps/lens/index.ts | 1 + .../apps/lens/show_underlying_data.ts | 178 ++++ 36 files changed, 2511 insertions(+), 99 deletions(-) create mode 100644 x-pack/plugins/lens/public/app_plugin/show_underlying_data.test.ts create mode 100644 x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts create mode 100644 x-pack/test/functional/apps/lens/show_underlying_data.ts diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx index b74fe5249e66c..1c55519e23256 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx @@ -24,6 +24,8 @@ export interface TopNavMenuData { isLoading?: boolean; iconType?: string; iconSide?: EuiButtonProps['iconSide']; + target?: string; + href?: string; } export interface RegisteredTopNavMenuData extends TopNavMenuData { diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx index 721a0fae0e62f..495e5c4ac9593 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx @@ -50,12 +50,19 @@ export function TopNavMenuItem(props: TopNavMenuData) { className: props.className, }; + // If the item specified a href, then override the suppress the onClick + // and make it become a regular link + const overrideProps = + props.target && props.href + ? { onClick: undefined, href: props.href, target: props.target } + : {}; + const btn = props.emphasize ? ( {getButtonContainer()} ) : ( - + {getButtonContainer()} ); diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts index ec4d03041df89..eee1a1027f541 100644 --- a/test/functional/services/filter_bar.ts +++ b/test/functional/services/filter_bar.ts @@ -103,6 +103,11 @@ export class FilterBarService extends FtrService { return filters.length; } + public async getFiltersLabel(): Promise { + const filters = await this.testSubjects.findAll('~filter'); + return Promise.all(filters.map((filter) => filter.getVisibleText())); + } + /** * Adds a filter to the filter bar. * diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index 1debe6e6141b2..17a58a0f96770 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -28,7 +28,8 @@ "taskManager", "globalSearch", "savedObjectsTagging", - "spaces" + "spaces", + "discover" ], "configPath": [ "xpack", diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx index 8e8b7045fc253..dcd328ff5c483 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -28,10 +28,16 @@ import { DispatchSetState, } from '../state_management'; import { getIndexPatternsObjects, getIndexPatternsIds, getResolvedDateRange } from '../utils'; +import { + combineQueryAndFilters, + getLayerMetaInfo, + getShowUnderlyingDataLabel, +} from './show_underlying_data'; function getLensTopNavConfig(options: { showSaveAndReturn: boolean; enableExportToCSV: boolean; + showOpenInDiscover?: boolean; showCancel: boolean; isByValueMode: boolean; allowByValue: boolean; @@ -46,6 +52,7 @@ function getLensTopNavConfig(options: { showCancel, allowByValue, enableExportToCSV, + showOpenInDiscover, showSaveAndReturn, savingToLibraryPermitted, savingToDashboardPermitted, @@ -90,6 +97,21 @@ function getLensTopNavConfig(options: { }); } + if (showOpenInDiscover) { + topNavMenu.push({ + label: getShowUnderlyingDataLabel(), + run: () => {}, + testId: 'lnsApp_openInDiscover', + description: i18n.translate('xpack.lens.app.openInDiscoverAriaLabel', { + defaultMessage: 'Open underlying data in Discover', + }), + disableButton: Boolean(tooltips.showUnderlyingDataWarning()), + tooltip: tooltips.showUnderlyingDataWarning, + target: '_blank', + href: actions.getUnderlyingDataUrl(), + }); + } + topNavMenu.push({ label: i18n.translate('xpack.lens.app.inspect', { defaultMessage: 'Inspect', @@ -183,6 +205,7 @@ export const LensTopNavMenu = ({ uiSettings, application, attributeService, + discover, dashboardFeatureFlag, } = useKibana().services; @@ -290,6 +313,26 @@ export const LensTopNavMenu = ({ filters, initialContext, ]); + + const layerMetaInfo = useMemo(() => { + if (!activeDatasourceId || !discover) { + return; + } + return getLayerMetaInfo( + datasourceMap[activeDatasourceId], + datasourceStates[activeDatasourceId].state, + activeData, + application.capabilities + ); + }, [ + activeData, + activeDatasourceId, + datasourceMap, + datasourceStates, + discover, + application.capabilities, + ]); + const topNavConfig = useMemo(() => { const baseMenuEntries = getLensTopNavConfig({ showSaveAndReturn: @@ -299,6 +342,7 @@ export const LensTopNavMenu = ({ (dashboardFeatureFlag.allowByValueEmbeddables || Boolean(initialInput)) ) || Boolean(initialContextIsEmbedded), enableExportToCSV: Boolean(isSaveable && activeData && Object.keys(activeData).length), + showOpenInDiscover: Boolean(layerMetaInfo?.isVisible), isByValueMode: getIsByValueMode(), allowByValue: dashboardFeatureFlag.allowByValueEmbeddables, showCancel: Boolean(isLinkedToOriginatingApp), @@ -321,6 +365,9 @@ export const LensTopNavMenu = ({ } return undefined; }, + showUnderlyingDataWarning: () => { + return layerMetaInfo?.error; + }, }, actions: { inspect: () => lensInspector.inspect({ title }), @@ -388,6 +435,31 @@ export const LensTopNavMenu = ({ redirectToOrigin(); } }, + getUnderlyingDataUrl: () => { + if (!layerMetaInfo) { + return; + } + const { error, meta } = layerMetaInfo; + // If Discover is not available, return + // If there's no data, return + if (error || !discover || !meta) { + return; + } + const { filters: newFilters, query: newQuery } = combineQueryAndFilters( + query, + filters, + meta, + indexPatterns + ); + + return discover.locator!.getRedirectUrl({ + indexPatternId: meta.id, + timeRange: data.query.timefilter.timefilter.getTime(), + filters: newFilters, + query: newQuery, + columns: meta.columns, + }); + }, }, }); return [...(additionalMenuEntries || []), ...baseMenuEntries]; @@ -398,6 +470,7 @@ export const LensTopNavMenu = ({ initialContextIsEmbedded, isSaveable, activeData, + layerMetaInfo, getIsByValueMode, savingToLibraryPermitted, savingToDashboardPermitted, @@ -414,6 +487,11 @@ export const LensTopNavMenu = ({ setIsSaveModalVisible, goBackToOriginatingApp, redirectToOrigin, + discover, + query, + filters, + indexPatterns, + data.query.timefilter.timefilter, ]); const onQuerySubmitWrapped = useCallback( diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 6f2fd4e8026ad..131dd5e66b6af 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -61,6 +61,7 @@ export async function getLensServices( usageCollection, fieldFormats, spaces, + discover, } = startDependencies; const storage = new Storage(localStorage); @@ -95,6 +96,7 @@ export async function getLensServices( // Temporarily required until the 'by value' paradigm is default. dashboardFeatureFlag: startDependencies.dashboard.dashboardFeatureFlagConfig, spaces, + discover, }; } @@ -114,8 +116,10 @@ export async function mountApp( getPresentationUtilContext, topNavMenuEntryGenerators, } = mountProps; - const [coreStart, startDependencies] = await core.getStartServices(); - const instance = await createEditorFrame(); + const [[coreStart, startDependencies], instance] = await Promise.all([ + core.getStartServices(), + createEditorFrame(), + ]); const historyLocationState = params.history.location.state as HistoryLocationState; const lensServices = await getLensServices(coreStart, startDependencies, attributeService); diff --git a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.test.ts b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.test.ts new file mode 100644 index 0000000000000..e74dd139e42c0 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.test.ts @@ -0,0 +1,602 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createMockDatasource } from '../mocks'; +import { combineQueryAndFilters, getLayerMetaInfo } from './show_underlying_data'; +import { Filter } from '@kbn/es-query'; +import { DatasourcePublicAPI } from '../types'; +import { RecursiveReadonly } from '@kbn/utility-types'; +import { Capabilities } from 'kibana/public'; + +describe('getLayerMetaInfo', () => { + const capabilities = { + navLinks: { discover: true }, + discover: { show: true }, + } as unknown as RecursiveReadonly; + it('should return error in case of no data', () => { + expect( + getLayerMetaInfo(createMockDatasource('testDatasource'), {}, undefined, capabilities).error + ).toBe('Visualization has no data available to show'); + }); + + it('should return error in case of multiple layers', () => { + expect( + getLayerMetaInfo( + createMockDatasource('testDatasource'), + {}, + { + datatable1: { type: 'datatable', columns: [], rows: [] }, + datatable2: { type: 'datatable', columns: [], rows: [] }, + }, + capabilities + ).error + ).toBe('Cannot show underlying data for visualizations with multiple layers'); + }); + + it('should return error in case of missing activeDatasource', () => { + expect(getLayerMetaInfo(undefined, {}, undefined, capabilities).error).toBe( + 'Visualization has no data available to show' + ); + }); + + it('should return error in case of missing configuration/state', () => { + expect( + getLayerMetaInfo(createMockDatasource('testDatasource'), undefined, {}, capabilities).error + ).toBe('Visualization has no data available to show'); + }); + + it('should return error in case of a timeshift declared in a column', () => { + const mockDatasource = createMockDatasource('testDatasource'); + const updatedPublicAPI: DatasourcePublicAPI = { + datasourceId: 'testDatasource', + getOperationForColumnId: jest.fn(() => ({ + dataType: 'number', + isBucketed: false, + scale: 'ratio', + label: 'A field', + isStaticValue: false, + sortingHint: undefined, + hasTimeShift: true, + })), + getTableSpec: jest.fn(), + getVisualDefaults: jest.fn(), + getSourceId: jest.fn(), + getFilters: jest.fn(), + }; + mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI); + expect( + getLayerMetaInfo(createMockDatasource('testDatasource'), {}, {}, capabilities).error + ).toBe('Visualization has no data available to show'); + }); + + it('should not be visible if discover is not available', () => { + // both capabilities should be enabled to enable discover + expect( + getLayerMetaInfo( + createMockDatasource('testDatasource'), + {}, + { + datatable1: { type: 'datatable', columns: [], rows: [] }, + }, + { + navLinks: { discover: false }, + discover: { show: true }, + } as unknown as RecursiveReadonly + ).isVisible + ).toBeFalsy(); + expect( + getLayerMetaInfo( + createMockDatasource('testDatasource'), + {}, + { + datatable1: { type: 'datatable', columns: [], rows: [] }, + }, + { + navLinks: { discover: true }, + discover: { show: false }, + } as unknown as RecursiveReadonly + ).isVisible + ).toBeFalsy(); + }); + + it('should basically work collecting fields and filters in the visualization', () => { + const mockDatasource = createMockDatasource('testDatasource'); + const updatedPublicAPI: DatasourcePublicAPI = { + datasourceId: 'indexpattern', + getOperationForColumnId: jest.fn(), + getTableSpec: jest.fn(() => [{ columnId: 'col1', fields: ['bytes'] }]), + getVisualDefaults: jest.fn(), + getSourceId: jest.fn(), + getFilters: jest.fn(() => ({ + kuery: [[{ language: 'kuery', query: 'memory > 40000' }]], + lucene: [], + })), + }; + mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI); + const { error, meta } = getLayerMetaInfo( + mockDatasource, + {}, // the publicAPI has been mocked, so no need for a state here + { + datatable1: { type: 'datatable', columns: [], rows: [] }, + }, + capabilities + ); + expect(error).toBeUndefined(); + expect(meta?.columns).toEqual(['bytes']); + expect(meta?.filters).toEqual({ + kuery: [ + [ + { + language: 'kuery', + query: 'memory > 40000', + }, + ], + ], + lucene: [], + }); + }); + + it('should return an error if datasource is not supported', () => { + const mockDatasource = createMockDatasource('testDatasource'); + const updatedPublicAPI: DatasourcePublicAPI = { + datasourceId: 'unsupportedDatasource', + getOperationForColumnId: jest.fn(), + getTableSpec: jest.fn(() => [{ columnId: 'col1', fields: ['bytes'] }]), + getVisualDefaults: jest.fn(), + getSourceId: jest.fn(), + getFilters: jest.fn(() => ({ + kuery: [[{ language: 'kuery', query: 'memory > 40000' }]], + lucene: [], + })), + }; + mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI); + const { error, meta } = getLayerMetaInfo( + mockDatasource, + {}, // the publicAPI has been mocked, so no need for a state here + { + datatable1: { type: 'datatable', columns: [], rows: [] }, + }, + capabilities + ); + expect(error).toBe('Underlying data does not support the current datasource'); + expect(meta).toBeUndefined(); + }); +}); +describe('combineQueryAndFilters', () => { + it('should just return same query and filters if no fields or filters are in layer meta', () => { + expect( + combineQueryAndFilters( + { language: 'kuery', query: 'myfield: *' }, + [], + { + id: 'testDatasource', + columns: [], + filters: { kuery: [], lucene: [] }, + }, + undefined + ) + ).toEqual({ query: { language: 'kuery', query: 'myfield: *' }, filters: [] }); + }); + + it('should concatenate filters with existing query if languages match (AND it)', () => { + expect( + combineQueryAndFilters( + { language: 'kuery', query: 'myfield: *' }, + [], + { + id: 'testDatasource', + columns: [], + filters: { kuery: [[{ language: 'kuery', query: 'otherField: *' }]], lucene: [] }, + }, + undefined + ) + ).toEqual({ + query: { language: 'kuery', query: '( myfield: * ) AND ( otherField: * )' }, + filters: [], + }); + }); + + it('should build single kuery expression from meta filters and assign it as query for final use', () => { + expect( + combineQueryAndFilters( + undefined, + [], + { + id: 'testDatasource', + columns: [], + filters: { kuery: [[{ language: 'kuery', query: 'otherField: *' }]], lucene: [] }, + }, + undefined + ) + ).toEqual({ query: { language: 'kuery', query: '( otherField: * )' }, filters: [] }); + }); + + it('should build single kuery expression from meta filters and join using OR and AND at the right level', () => { + // OR queries from the same array, AND queries from different arrays + expect( + combineQueryAndFilters( + undefined, + [], + { + id: 'testDatasource', + columns: [], + filters: { + kuery: [ + [ + { language: 'kuery', query: 'myfield: *' }, + { language: 'kuery', query: 'otherField: *' }, + ], + [ + { language: 'kuery', query: 'myfieldCopy: *' }, + { language: 'kuery', query: 'otherFieldCopy: *' }, + ], + ], + lucene: [], + }, + }, + undefined + ) + ).toEqual({ + query: { + language: 'kuery', + query: + '( ( ( myfield: * ) OR ( otherField: * ) ) AND ( ( myfieldCopy: * ) OR ( otherFieldCopy: * ) ) )', + }, + filters: [], + }); + }); + it('should assign kuery meta filters to app filters if existing query is using lucene language', () => { + expect( + combineQueryAndFilters( + { language: 'lucene', query: 'myField' }, + [], + { + id: 'testDatasource', + columns: [], + filters: { + kuery: [[{ language: 'kuery', query: 'myfield: *' }]], + lucene: [], + }, + }, + undefined + ) + ).toEqual({ + query: { language: 'lucene', query: 'myField' }, + filters: [ + { + $state: { + store: 'appState', + }, + bool: { + filter: [ + { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'myfield', + }, + }, + ], + }, + }, + ], + must: [], + must_not: [], + should: [], + }, + meta: { + alias: 'Lens context (kuery)', + disabled: false, + index: 'testDatasource', + negate: false, + type: 'custom', + }, + }, + ], + }); + }); + it('should append lucene meta filters to app filters even if existing filters are using kuery', () => { + expect( + combineQueryAndFilters( + { language: 'kuery', query: 'myField: *' }, + [ + { + $state: { + store: 'appState', + }, + bool: { + filter: [ + { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'myfield', + }, + }, + ], + }, + }, + ], + must: [], + must_not: [], + should: [], + }, + meta: { + alias: 'Existing kuery filters', + disabled: false, + index: 'testDatasource', + negate: false, + type: 'custom', + }, + } as Filter, + ], + { + id: 'testDatasource', + columns: [], + filters: { + kuery: [], + lucene: [[{ language: 'lucene', query: 'anotherField' }]], + }, + }, + undefined + ) + ).toEqual({ + filters: [ + { + $state: { + store: 'appState', + }, + bool: { + filter: [ + { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'myfield', + }, + }, + ], + }, + }, + ], + must: [], + must_not: [], + should: [], + }, + meta: { + alias: 'Existing kuery filters', + disabled: false, + index: 'testDatasource', + negate: false, + type: 'custom', + }, + }, + { + $state: { + store: 'appState', + }, + bool: { + filter: [], + must: [ + { + query_string: { + query: '( anotherField )', + }, + }, + ], + must_not: [], + should: [], + }, + meta: { + alias: 'Lens context (lucene)', + disabled: false, + index: 'testDatasource', + negate: false, + type: 'custom', + }, + }, + ], + query: { + language: 'kuery', + query: 'myField: *', + }, + }); + }); + it('should append lucene meta filters to an existing lucene query', () => { + expect( + combineQueryAndFilters( + { language: 'lucene', query: 'myField' }, + [], + { + id: 'testDatasource', + columns: [], + filters: { + kuery: [[{ language: 'kuery', query: 'myfield: *' }]], + lucene: [[{ language: 'lucene', query: 'anotherField' }]], + }, + }, + undefined + ) + ).toEqual({ + filters: [ + { + $state: { + store: 'appState', + }, + bool: { + filter: [ + { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'myfield', + }, + }, + ], + }, + }, + ], + must: [], + must_not: [], + should: [], + }, + meta: { + alias: 'Lens context (kuery)', + disabled: false, + index: 'testDatasource', + negate: false, + type: 'custom', + }, + }, + ], + query: { + language: 'lucene', + query: '( myField ) AND ( anotherField )', + }, + }); + }); + it('should work for complex cases of nested meta filters', () => { + // scenario overview: + // A kuery query + // A kuery filter pill + // 4 kuery table filter groups (1 from filtered column, 2 from filters, 1 from top values, 1 from custom ranges) + // 2 lucene table filter groups (1 from filtered column + 2 from filters ) + expect( + combineQueryAndFilters( + { language: 'kuery', query: 'myField: *' }, + [ + { + $state: { + store: 'appState', + }, + bool: { + filter: [ + { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'myfield', + }, + }, + ], + }, + }, + ], + must: [], + must_not: [], + should: [], + }, + meta: { + alias: 'Existing kuery filters', + disabled: false, + index: 'testDatasource', + negate: false, + type: 'custom', + }, + } as Filter, + ], + { + id: 'testDatasource', + columns: [], + filters: { + kuery: [ + [{ language: 'kuery', query: 'bytes > 4000' }], + [ + { language: 'kuery', query: 'memory > 5000' }, + { language: 'kuery', query: 'memory >= 15000' }, + ], + [{ language: 'kuery', query: 'myField: *' }], + [{ language: 'kuery', query: 'otherField >= 15' }], + ], + lucene: [ + [{ language: 'lucene', query: 'filteredField: 400' }], + [ + { language: 'lucene', query: 'aNewField' }, + { language: 'lucene', query: 'anotherNewField: 200' }, + ], + ], + }, + }, + undefined + ) + ).toEqual({ + filters: [ + { + $state: { + store: 'appState', + }, + bool: { + filter: [ + { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'myfield', + }, + }, + ], + }, + }, + ], + must: [], + must_not: [], + should: [], + }, + meta: { + alias: 'Existing kuery filters', + disabled: false, + index: 'testDatasource', + negate: false, + type: 'custom', + }, + }, + { + $state: { + store: 'appState', + }, + bool: { + filter: [], + must: [ + { + query_string: { + query: + '( ( filteredField: 400 ) AND ( ( aNewField ) OR ( anotherNewField: 200 ) ) )', + }, + }, + ], + must_not: [], + should: [], + }, + meta: { + alias: 'Lens context (lucene)', + disabled: false, + index: 'testDatasource', + negate: false, + type: 'custom', + }, + }, + ], + query: { + language: 'kuery', + query: + '( myField: * ) AND ( ( bytes > 4000 ) AND ( ( memory > 5000 ) OR ( memory >= 15000 ) ) AND ( myField: * ) AND ( otherField >= 15 ) )', + }, + }); + }); +}); diff --git a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts new file mode 100644 index 0000000000000..e1956542f8def --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + Query, + Filter, + DataViewBase, + buildCustomFilter, + buildEsQuery, + FilterStateStore, +} from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; +import { RecursiveReadonly } from '@kbn/utility-types'; +import { Capabilities } from 'kibana/public'; +import { TableInspectorAdapter } from '../editor_frame_service/types'; +import { Datasource } from '../types'; + +export const getShowUnderlyingDataLabel = () => + i18n.translate('xpack.lens.app.openInDiscover', { + defaultMessage: 'Open in Discover', + }); + +function joinQueries(queries: Query[][] | undefined) { + if (!queries) { + return ''; + } + const expression = queries + .filter((subQueries) => subQueries.length) + .map((subQueries) => + // reduce the amount of round brackets in case of one query + subQueries.length > 1 + ? `( ${subQueries.map(({ query: filterQuery }) => `( ${filterQuery} )`).join(' OR ')} )` + : `( ${subQueries[0].query} )` + ) + .join(' AND '); + return queries.length > 1 ? `( ${expression} )` : expression; +} + +interface LayerMetaInfo { + id: string; + columns: string[]; + filters: { + kuery: Query[][] | undefined; + lucene: Query[][] | undefined; + }; +} + +export function getLayerMetaInfo( + currentDatasource: Datasource | undefined, + datasourceState: unknown, + activeData: TableInspectorAdapter | undefined, + capabilities: RecursiveReadonly +): { meta: LayerMetaInfo | undefined; isVisible: boolean; error: string | undefined } { + const isVisible = Boolean(capabilities.navLinks?.discover && capabilities.discover?.show); + // If Multiple tables, return + // If there are time shifts, return + const [datatable, ...otherTables] = Object.values(activeData || {}); + if (!datatable || !currentDatasource || !datasourceState) { + return { + meta: undefined, + error: i18n.translate('xpack.lens.app.showUnderlyingDataNoData', { + defaultMessage: 'Visualization has no data available to show', + }), + isVisible, + }; + } + if (otherTables.length) { + return { + meta: undefined, + error: i18n.translate('xpack.lens.app.showUnderlyingDataMultipleLayers', { + defaultMessage: 'Cannot show underlying data for visualizations with multiple layers', + }), + isVisible, + }; + } + const [firstLayerId] = currentDatasource.getLayers(datasourceState); + const datasourceAPI = currentDatasource.getPublicAPI({ + layerId: firstLayerId, + state: datasourceState, + }); + // maybe add also datasourceId validation here? + if (datasourceAPI.datasourceId !== 'indexpattern') { + return { + meta: undefined, + error: i18n.translate('xpack.lens.app.showUnderlyingDataUnsupportedDatasource', { + defaultMessage: 'Underlying data does not support the current datasource', + }), + isVisible, + }; + } + const tableSpec = datasourceAPI.getTableSpec(); + + const columnsWithNoTimeShifts = tableSpec.filter( + ({ columnId }) => !datasourceAPI.getOperationForColumnId(columnId)?.hasTimeShift + ); + if (columnsWithNoTimeShifts.length < tableSpec.length) { + return { + meta: undefined, + error: i18n.translate('xpack.lens.app.showUnderlyingDataTimeShifts', { + defaultMessage: "Cannot show underlying data when there's a time shift configured", + }), + isVisible, + }; + } + + const uniqueFields = [...new Set(columnsWithNoTimeShifts.map(({ fields }) => fields).flat())]; + return { + meta: { + id: datasourceAPI.getSourceId()!, + columns: uniqueFields, + filters: datasourceAPI.getFilters(activeData), + }, + error: undefined, + isVisible, + }; +} + +// This enforces on assignment time that the two props are not the same +type LanguageAssignments = + | { queryLanguage: 'lucene'; filtersLanguage: 'kuery' } + | { queryLanguage: 'kuery'; filtersLanguage: 'lucene' }; + +export function combineQueryAndFilters( + query: Query | undefined, + filters: Filter[], + meta: LayerMetaInfo, + dataViews: DataViewBase[] | undefined +) { + // Unless a lucene query is already defined, kuery is assigned to it + const { queryLanguage, filtersLanguage }: LanguageAssignments = + query?.language === 'lucene' + ? { queryLanguage: 'lucene', filtersLanguage: 'kuery' } + : { queryLanguage: 'kuery', filtersLanguage: 'lucene' }; + + let newQuery = query; + if (meta.filters[queryLanguage]?.length) { + const filtersQuery = joinQueries(meta.filters[queryLanguage]); + newQuery = { + language: queryLanguage, + query: query?.query.trim() + ? `( ${query.query} ) ${filtersQuery ? `AND ${filtersQuery}` : ''}` + : filtersQuery, + }; + } + + // make a copy as the original filters are readonly + const newFilters = [...filters]; + if (meta.filters[filtersLanguage]?.length) { + const queryExpression = joinQueries(meta.filters[filtersLanguage]); + // Append the new filter based on the queryExpression to the existing ones + newFilters.push( + buildCustomFilter( + meta.id!, + buildEsQuery( + dataViews?.find(({ id }) => id === meta.id), + { language: filtersLanguage, query: queryExpression }, + [] + ), + false, + false, + i18n.translate('xpack.lens.app.lensContext', { + defaultMessage: 'Lens context ({language})', + values: { language: filtersLanguage }, + }), + FilterStateStore.APP_STATE + ) + ); + } + return { filters: newFilters, query: newQuery }; +} diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index 25fff038c4814..003e458b8114d 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -7,6 +7,7 @@ import type { History } from 'history'; import type { OnSaveProps } from 'src/plugins/saved_objects/public'; +import { DiscoverStart } from 'src/plugins/discover/public'; import { SpacesApi } from '../../../spaces/public'; import type { ApplicationStart, @@ -135,6 +136,7 @@ export interface LensAppServices { getOriginatingAppName: () => string | undefined; presentationUtil: PresentationUtilPluginStart; spaces: SpacesApi; + discover?: DiscoverStart; // Temporarily required until the 'by value' paradigm is default. dashboardFeatureFlag: DashboardFeatureFlagConfig; @@ -142,6 +144,7 @@ export interface LensAppServices { export interface LensTopNavTooltips { showExportWarning: () => string | undefined; + showUnderlyingDataWarning: () => string | undefined; } export interface LensTopNavActions { @@ -151,4 +154,5 @@ export interface LensTopNavActions { goBack: () => void; cancel: () => void; exportToCSV: () => void; + getUnderlyingDataUrl: () => string | undefined; } diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx index d8dabd81441da..b6c72cc5fe6fb 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/dimension_editor.test.tsx @@ -7,7 +7,11 @@ import React from 'react'; import { EuiButtonGroup, EuiComboBox, EuiFieldText } from '@elastic/eui'; -import { FramePublicAPI, Operation, VisualizationDimensionEditorProps } from '../../types'; +import { + FramePublicAPI, + OperationDescriptor, + VisualizationDimensionEditorProps, +} from '../../types'; import { DatatableVisualizationState } from '../visualization'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { mountWithIntl } from '@kbn/test-jest-helpers'; @@ -218,7 +222,7 @@ describe('data table dimension editor', () => { it('should not show the dynamic coloring option for a bucketed operation', () => { frame.activeData!.first.columns[0].meta.type = 'number'; frame.datasourceLayers.first.getOperationForColumnId = jest.fn( - () => ({ isBucketed: true } as Operation) + () => ({ isBucketed: true } as OperationDescriptor) ); state.columns[0].colorMode = 'cell'; const instance = mountWithIntl(); diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx index d03263305bb90..13b6581e99d2a 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx @@ -370,7 +370,10 @@ describe('Datatable Visualization', () => { const datasource = createMockDatasource('test'); const frame = mockFrame(); frame.datasourceLayers = { a: datasource.publicAPIMock }; - datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]); + datasource.publicAPIMock.getTableSpec.mockReturnValue([ + { columnId: 'c', fields: [] }, + { columnId: 'b', fields: [] }, + ]); expect( datatableVisualization.getConfiguration({ @@ -501,7 +504,10 @@ describe('Datatable Visualization', () => { beforeEach(() => { datasource = createMockDatasource('test'); - datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]); + datasource.publicAPIMock.getTableSpec.mockReturnValue([ + { columnId: 'c', fields: [] }, + { columnId: 'b', fields: [] }, + ]); frame = mockFrame(); frame.datasourceLayers = { a: datasource.publicAPIMock }; @@ -512,6 +518,8 @@ describe('Datatable Visualization', () => { dataType: 'string', isBucketed: false, // <= make them metrics label: 'label', + isStaticValue: false, + hasTimeShift: false, }); const expression = datatableVisualization.toExpression( @@ -559,6 +567,8 @@ describe('Datatable Visualization', () => { dataType: 'string', isBucketed: true, // move it from the metric to the break down by side label: 'label', + isStaticValue: false, + hasTimeShift: false, }); const expression = datatableVisualization.toExpression( @@ -609,11 +619,16 @@ describe('Datatable Visualization', () => { const datasource = createMockDatasource('test'); const frame = mockFrame(); frame.datasourceLayers = { a: datasource.publicAPIMock }; - datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]); + datasource.publicAPIMock.getTableSpec.mockReturnValue([ + { columnId: 'c', fields: [] }, + { columnId: 'b', fields: [] }, + ]); datasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', isBucketed: true, // move it from the metric to the break down by side label: 'label', + isStaticValue: false, + hasTimeShift: false, }); const error = datatableVisualization.getErrorMessages({ @@ -629,11 +644,16 @@ describe('Datatable Visualization', () => { const datasource = createMockDatasource('test'); const frame = mockFrame(); frame.datasourceLayers = { a: datasource.publicAPIMock }; - datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]); + datasource.publicAPIMock.getTableSpec.mockReturnValue([ + { columnId: 'c', fields: [] }, + { columnId: 'b', fields: [] }, + ]); datasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', isBucketed: false, // keep it a metric label: 'label', + isStaticValue: false, + hasTimeShift: false, }); const error = datatableVisualization.getErrorMessages({ diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index 5bc6a69b2efaf..e660df8ff7bb9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -155,7 +155,13 @@ export function DimensionContainer({
{panel}
- + {i18n.translate('xpack.lens.dimensionContainer.close', { defaultMessage: 'Close', })} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx index 174bb48bc9e41..a54161863ed24 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx @@ -476,6 +476,8 @@ describe('editor_frame', () => { getOperationForColumnId: jest.fn(), getTableSpec: jest.fn(), getVisualDefaults: jest.fn(), + getSourceId: jest.fn(), + getFilters: jest.fn(), }; mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts index 48536f8599060..0167f6e4b5a43 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts @@ -702,10 +702,12 @@ describe('suggestion helpers', () => { defaultParams = [ { '1': { - getTableSpec: () => [{ columnId: 'col1' }], + getTableSpec: () => [{ columnId: 'col1', fields: [] }], datasourceId: '', getOperationForColumnId: jest.fn(), getVisualDefaults: jest.fn(), + getSourceId: jest.fn(), + getFilters: jest.fn(), }, }, { activeId: 'testVis', state: {} }, @@ -764,6 +766,8 @@ describe('suggestion helpers', () => { datasourceId: '', getOperationForColumnId: jest.fn(), getVisualDefaults: jest.fn(), + getSourceId: jest.fn(), + getFilters: jest.fn(), }, }; mockVisualization1.getSuggestions.mockReturnValue([]); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx index a486b6315c3f4..9288b49824dc2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx @@ -271,9 +271,9 @@ describe('chart_switch', () => { }, ]); datasourceMap.testDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'col1' }, - { columnId: 'col2' }, - { columnId: 'col3' }, + { columnId: 'col1', fields: [] }, + { columnId: 'col2', fields: [] }, + { columnId: 'col3', fields: [] }, ]); const { instance } = await mountWithProvider( diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts b/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts index a08b12ca9ae6d..a3d97cdda00fb 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/heatmap_visualization/visualization.test.ts @@ -20,7 +20,7 @@ import { } from './constants'; import { Position } from '@elastic/charts'; import type { HeatmapVisualizationState } from './types'; -import type { DatasourcePublicAPI, Operation } from '../types'; +import type { DatasourcePublicAPI, OperationDescriptor } from '../types'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { layerTypes } from '../../common'; import { themeServiceMock } from '../../../../../src/core/public/mocks'; @@ -99,7 +99,7 @@ describe('heatmap', () => { mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', label: 'MyOperation', - } as Operation); + } as OperationDescriptor); frame.datasourceLayers = { first: mockDatasource.publicAPIMock, @@ -363,7 +363,7 @@ describe('heatmap', () => { mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', label: 'MyOperation', - } as Operation); + } as OperationDescriptor); datasourceLayers = { first: mockDatasource.publicAPIMock, @@ -483,7 +483,7 @@ describe('heatmap', () => { mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', label: 'MyOperation', - } as Operation); + } as OperationDescriptor); datasourceLayers = { first: mockDatasource.publicAPIMock, @@ -612,7 +612,7 @@ describe('heatmap', () => { mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', label: 'MyOperation', - } as Operation); + } as OperationDescriptor); frame.datasourceLayers = { first: mockDatasource.publicAPIMock, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/filtering.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/filtering.tsx index 11e9110171f40..f776e0415f1b6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/filtering.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/filtering.tsx @@ -166,10 +166,10 @@ export function Filtering({ isInvalid={!isQueryInputValid} error={queryInputError} fullWidth={true} + data-test-subj="indexPattern-filter-by-input" > { describe('getTableSpec', () => { it('should include col1', () => { - expect(publicAPI.getTableSpec()).toEqual([{ columnId: 'col1' }]); + expect(publicAPI.getTableSpec()).toEqual([expect.objectContaining({ columnId: 'col1' })]); + }); + + it('should include fields prop for each column', () => { + expect(publicAPI.getTableSpec()).toEqual([expect.objectContaining({ fields: ['op'] })]); }); it('should skip columns that are being referenced', () => { @@ -1252,7 +1258,98 @@ describe('IndexPattern Data Source', () => { layerId: 'first', }); - expect(publicAPI.getTableSpec()).toEqual([{ columnId: 'col2' }]); + expect(publicAPI.getTableSpec()).toEqual([expect.objectContaining({ columnId: 'col2' })]); + }); + + it('should collect all fields (also from referenced columns)', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + + operationType: 'sum', + sourceField: 'test', + params: {}, + } as GenericIndexPatternColumn, + col2: { + label: 'Cumulative sum', + dataType: 'number', + isBucketed: false, + + operationType: 'cumulative_sum', + references: ['col1'], + params: {}, + } as GenericIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + // The cumulative sum column has no field, but it references a sum column (hidden) which has it + // The getTableSpec() should walk the reference tree and assign all fields to the root column + expect(publicAPI.getTableSpec()).toEqual([{ columnId: 'col2', fields: ['test'] }]); + }); + + it('should collect and organize fields per visible column', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2', 'col3'], + columns: { + col1: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + + operationType: 'sum', + sourceField: 'test', + params: {}, + } as GenericIndexPatternColumn, + col2: { + label: 'Cumulative sum', + dataType: 'number', + isBucketed: false, + + operationType: 'cumulative_sum', + references: ['col1'], + params: {}, + } as GenericIndexPatternColumn, + col3: { + label: 'My Op', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'op', + params: { + size: 5, + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + }, + } as TermsIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + + // col1 is skipped as referenced but its field gets inherited by col2 + expect(publicAPI.getTableSpec()).toEqual([ + { columnId: 'col2', fields: ['test'] }, + { columnId: 'col3', fields: ['op'] }, + ]); }); }); @@ -1263,7 +1360,8 @@ describe('IndexPattern Data Source', () => { dataType: 'string', isBucketed: true, isStaticValue: false, - } as Operation); + hasTimeShift: false, + } as OperationDescriptor); }); it('should return null for non-existant columns', () => { @@ -1306,6 +1404,752 @@ describe('IndexPattern Data Source', () => { expect(publicAPI.getOperationForColumnId('col1')).toEqual(null); }); }); + + describe('getSourceId', () => { + it('should basically return the datasource internal id', () => { + expect(publicAPI.getSourceId()).toEqual('1'); + }); + }); + + describe('getFilters', () => { + it('should return all filters in metrics, grouped by language', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + operationType: 'sum', + sourceField: 'test', + params: {}, + filter: { language: 'kuery', query: 'bytes > 1000' }, + } as GenericIndexPatternColumn, + col2: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + operationType: 'sum', + sourceField: 'test', + params: {}, + filter: { language: 'lucene', query: 'memory' }, + } as GenericIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [[{ language: 'kuery', query: 'bytes > 1000' }]], + lucene: [[{ language: 'lucene', query: 'memory' }]], + }); + }); + it('should ignore empty filtered metrics', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + operationType: 'sum', + sourceField: 'test', + params: {}, + filter: { language: 'kuery', query: '' }, + } as GenericIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ kuery: [], lucene: [] }); + }); + it('shuold collect top values fields as kuery existence filters if no data is provided', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.src', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + }, + } as TermsIndexPatternColumn, + col2: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.dest', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + secondaryFields: ['myField'], + }, + } as TermsIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [ + [{ language: 'kuery', query: 'geo.src: *' }], + [ + { language: 'kuery', query: 'geo.dest: *' }, + { language: 'kuery', query: 'myField: *' }, + ], + ], + lucene: [], + }); + }); + it('shuold collect top values fields and terms as kuery filters if data is provided', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.src', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + }, + } as TermsIndexPatternColumn, + col2: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.dest', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + secondaryFields: ['myField'], + }, + } as TermsIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + const data = { + first: { + type: 'datatable' as const, + columns: [ + { id: 'col1', name: 'geo.src', meta: { type: 'string' as const } }, + { id: 'col2', name: 'geo.dest > myField', meta: { type: 'string' as const } }, + ], + rows: [ + { col1: 'US', col2: { keys: ['IT', 'MyValue'] } }, + { col1: 'IN', col2: { keys: ['DE', 'MyOtherValue'] } }, + ], + }, + }; + expect(publicAPI.getFilters(data)).toEqual({ + kuery: [ + [ + { language: 'kuery', query: 'geo.src: "US"' }, + { language: 'kuery', query: 'geo.src: "IN"' }, + ], + [ + { language: 'kuery', query: 'geo.dest: "IT" AND myField: "MyValue"' }, + { language: 'kuery', query: 'geo.dest: "DE" AND myField: "MyOtherValue"' }, + ], + ], + lucene: [], + }); + }); + it('shuold collect top values fields and terms and carefully handle empty string values', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.src', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + }, + } as TermsIndexPatternColumn, + col2: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.dest', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + secondaryFields: ['myField'], + }, + } as TermsIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + const data = { + first: { + type: 'datatable' as const, + columns: [ + { id: 'col1', name: 'geo.src', meta: { type: 'string' as const } }, + { id: 'col2', name: 'geo.dest > myField', meta: { type: 'string' as const } }, + ], + rows: [ + { col1: 'US', col2: { keys: ['IT', ''] } }, + { col1: 'IN', col2: { keys: ['DE', 'MyOtherValue'] } }, + ], + }, + }; + expect(publicAPI.getFilters(data)).toEqual({ + kuery: [ + [ + { language: 'kuery', query: 'geo.src: "US"' }, + { language: 'kuery', query: 'geo.src: "IN"' }, + ], + [ + { language: 'kuery', query: `geo.dest: "IT" AND myField: ""` }, + { language: 'kuery', query: `geo.dest: "DE" AND myField: "MyOtherValue"` }, + ], + ], + lucene: [], + }); + }); + it('should ignore top values fields if other/missing option is enabled', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.src', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + otherBucket: true, + }, + } as TermsIndexPatternColumn, + col2: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.src', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + missingBucket: true, + }, + } as TermsIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ kuery: [], lucene: [] }); + }); + it('should collect custom ranges as kuery filters', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Single range', + dataType: 'number', + isBucketed: true, + operationType: 'range', + sourceField: 'bytes', + params: { + type: 'range', + ranges: [{ from: 100, to: 150, label: 'Range 1' }], + }, + } as RangeIndexPatternColumn, + col2: { + label: 'Multiple ranges', + dataType: 'number', + isBucketed: true, + operationType: 'range', + sourceField: 'bytes', + params: { + type: 'range', + ranges: [ + { from: 200, to: 300, label: 'Range 2' }, + { from: 300, to: 400, label: 'Range 3' }, + ], + }, + } as RangeIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [ + [{ language: 'kuery', query: 'bytes >= 100 AND bytes <= 150' }], + [ + { language: 'kuery', query: 'bytes >= 200 AND bytes <= 300' }, + { language: 'kuery', query: 'bytes >= 300 AND bytes <= 400' }, + ], + ], + lucene: [], + }); + }); + it('should collect custom ranges as kuery filters as partial', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2', 'col3'], + columns: { + col1: { + label: 'Empty range', + dataType: 'number', + isBucketed: true, + operationType: 'range', + sourceField: 'bytes', + params: { + type: 'range', + ranges: [{ label: 'Empty range' }], + }, + } as RangeIndexPatternColumn, + col2: { + label: 'From range', + dataType: 'number', + isBucketed: true, + operationType: 'range', + sourceField: 'bytes', + params: { + type: 'range', + ranges: [{ from: 100, label: 'Partial range 1' }], + }, + } as RangeIndexPatternColumn, + col3: { + label: 'To ranges', + dataType: 'number', + isBucketed: true, + operationType: 'range', + sourceField: 'bytes', + params: { + type: 'range', + ranges: [{ to: 300, label: 'Partial Range 2' }], + }, + } as RangeIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [ + [{ language: 'kuery', query: 'bytes >= 100' }], + [{ language: 'kuery', query: 'bytes <= 300' }], + ], + lucene: [], + }); + }); + it('should collect filters within filters operation grouped by language', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2', 'col3'], + columns: { + col1: { + label: 'kuery Filter', + dataType: 'string', + isBucketed: true, + operationType: 'filters', + scale: 'ordinal', + params: { + filters: [{ label: '', input: { language: 'kuery', query: 'bytes > 1000' } }], + }, + } as FiltersIndexPatternColumn, + col2: { + label: 'Lucene Filter', + dataType: 'string', + isBucketed: true, + operationType: 'filters', + scale: 'ordinal', + params: { + filters: [{ label: '', input: { language: 'lucene', query: 'memory' } }], + }, + } as FiltersIndexPatternColumn, + col3: { + label: 'Mixed filters', + dataType: 'string', + isBucketed: true, + operationType: 'filters', + scale: 'ordinal', + params: { + filters: [ + { label: '', input: { language: 'kuery', query: 'bytes > 5000' } }, + { label: '', input: { language: 'kuery', query: 'memory > 500000' } }, + { label: '', input: { language: 'lucene', query: 'phpmemory' } }, + { label: '', input: { language: 'lucene', query: 'memory: 5000000' } }, + ], + }, + } as FiltersIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [ + [{ language: 'kuery', query: 'bytes > 1000' }], + [ + { language: 'kuery', query: 'bytes > 5000' }, + { language: 'kuery', query: 'memory > 500000' }, + ], + ], + lucene: [ + [{ language: 'lucene', query: 'memory' }], + [ + { language: 'lucene', query: 'phpmemory' }, + { language: 'lucene', query: 'memory: 5000000' }, + ], + ], + }); + }); + it('should ignore filtered metrics if at least one metric is unfiltered', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + operationType: 'sum', + sourceField: 'test', + params: {}, + filter: { language: 'kuery', query: 'bytes > 1000' }, + } as GenericIndexPatternColumn, + col2: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + operationType: 'sum', + sourceField: 'test', + params: {}, + } as GenericIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [], + lucene: [], + }); + }); + it('should ignore filtered metrics if at least one metric is unfiltered in formula', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['formula'], + columns: { + formula: { + label: 'Formula', + dataType: 'number', + operationType: 'formula', + isBucketed: false, + scale: 'ratio', + params: { + formula: "count(kql='memory > 5000') + count()", + isFormulaBroken: false, + }, + references: ['math'], + } as FormulaIndexPatternColumn, + countX0: { + label: 'countX0', + dataType: 'number', + operationType: 'count', + isBucketed: false, + scale: 'ratio', + sourceField: '___records___', + customLabel: true, + filter: { language: 'kuery', query: 'memory > 5000' }, + }, + countX1: { + label: 'countX1', + dataType: 'number', + operationType: 'count', + isBucketed: false, + scale: 'ratio', + sourceField: '___records___', + customLabel: true, + }, + math: { + label: 'math', + dataType: 'number', + operationType: 'math', + isBucketed: false, + scale: 'ratio', + params: { + tinymathAst: { + type: 'function', + name: 'add', + args: ['countX0', 'countX1'] as unknown as TinymathAST[], + location: { + min: 0, + max: 17, + }, + text: "count(kql='memory > 5000') + count()", + }, + }, + references: ['countX0', 'countX1'], + customLabel: true, + } as MathIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [], + lucene: [], + }); + }); + it('should support complete scenarios', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2', 'col3', 'col4'], + columns: { + col1: { + label: 'Mixed filters', + dataType: 'string', + isBucketed: true, + operationType: 'filters', + scale: 'ordinal', + params: { + filters: [ + { label: '', input: { language: 'kuery', query: 'bytes > 5000' } }, + { label: '', input: { language: 'kuery', query: 'memory > 500000' } }, + { label: '', input: { language: 'lucene', query: 'phpmemory' } }, + { label: '', input: { language: 'lucene', query: 'memory: 5000000' } }, + ], + }, + } as FiltersIndexPatternColumn, + col2: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + operationType: 'sum', + sourceField: 'test', + params: {}, + filter: { language: 'kuery', query: 'bytes > 1000' }, + } as GenericIndexPatternColumn, + col3: { + label: 'Sum', + dataType: 'number', + isBucketed: false, + operationType: 'sum', + sourceField: 'test', + params: {}, + filter: { language: 'lucene', query: 'memory' }, + } as GenericIndexPatternColumn, + col4: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'geo.src', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + secondaryFields: ['myField'], + }, + } as TermsIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [ + [{ language: 'kuery', query: 'bytes > 1000' }], + [ + { language: 'kuery', query: 'bytes > 5000' }, + { language: 'kuery', query: 'memory > 500000' }, + ], + [ + { language: 'kuery', query: 'geo.src: *' }, + { language: 'kuery', query: 'myField: *' }, + ], + ], + lucene: [ + [{ language: 'lucene', query: 'memory' }], + [ + { language: 'lucene', query: 'phpmemory' }, + { language: 'lucene', query: 'memory: 5000000' }, + ], + ], + }); + }); + + it('should avoid duplicate filters when formula has a global filter', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['formula'], + columns: { + formula: { + label: 'Formula', + dataType: 'number', + operationType: 'formula', + isBucketed: false, + scale: 'ratio', + filter: { language: 'kuery', query: 'bytes > 4000' }, + params: { + formula: "count(kql='memory > 5000') + count()", + isFormulaBroken: false, + }, + references: ['math'], + } as FormulaIndexPatternColumn, + countX0: { + label: 'countX0', + dataType: 'number', + operationType: 'count', + isBucketed: false, + scale: 'ratio', + sourceField: '___records___', + customLabel: true, + filter: { language: 'kuery', query: 'bytes > 4000 AND memory > 5000' }, + }, + countX1: { + label: 'countX1', + dataType: 'number', + operationType: 'count', + isBucketed: false, + scale: 'ratio', + sourceField: '___records___', + customLabel: true, + filter: { language: 'kuery', query: 'bytes > 4000' }, + }, + math: { + label: 'math', + dataType: 'number', + operationType: 'math', + isBucketed: false, + scale: 'ratio', + params: { + tinymathAst: { + type: 'function', + name: 'add', + args: ['countX0', 'countX1'] as unknown as TinymathAST[], + location: { + min: 0, + max: 17, + }, + text: "count(kql='memory > 5000') + count()", + }, + }, + references: ['countX0', 'countX1'], + customLabel: true, + } as MathIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + }); + expect(publicAPI.getFilters()).toEqual({ + kuery: [ + [ + { language: 'kuery', query: 'bytes > 4000 AND memory > 5000' }, + { language: 'kuery', query: 'bytes > 4000' }, + ], + ], + lucene: [], + }); + }); + }); }); describe('#getErrorMessages', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index f40f3b9623ca8..3578796ab1d6a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -18,10 +18,11 @@ import type { DatasourceDimensionEditorProps, DatasourceDimensionTriggerProps, DatasourceDataPanelProps, - Operation, DatasourceLayerPanelProps, PublicAPIProps, InitializationOptions, + OperationDescriptor, + FramePublicAPI, } from '../types'; import { loadInitialState, @@ -45,7 +46,7 @@ import { getDatasourceSuggestionsForVisualizeCharts, } from './indexpattern_suggestions'; -import { getVisualDefaultsForLayer, isColumnInvalid } from './utils'; +import { getFiltersInLayer, getVisualDefaultsForLayer, isColumnInvalid } from './utils'; import { normalizeOperationDataType, isDraggedField } from './pure_utils'; import { LayerPanel } from './layerpanel'; import { @@ -53,7 +54,9 @@ import { GenericIndexPatternColumn, getErrorMessages, insertNewColumn, + TermsIndexPatternColumn, } from './operations'; +import { getReferenceRoot } from './operations/layer_helpers'; import { IndexPatternField, IndexPatternPrivateState, @@ -75,6 +78,7 @@ import { GeoFieldWorkspacePanel } from '../editor_frame_service/editor_frame/wor import { DraggingIdentifier } from '../drag_drop'; import { getStateTimeShiftWarningMessages } from './time_shift_utils'; import { getPrecisionErrorWarningMessages } from './utils'; +import { DOCUMENT_FIELD_NAME } from '../../common/constants'; import { isColumnOfType } from './operations/definitions/helpers'; export type { OperationType, GenericIndexPatternColumn } from './operations'; export { deleteColumn } from './operations'; @@ -83,8 +87,8 @@ export function columnToOperation( column: GenericIndexPatternColumn, uniqueLabel?: string, dataView?: IndexPattern -): Operation { - const { dataType, label, isBucketed, scale, operationType } = column; +): OperationDescriptor { + const { dataType, label, isBucketed, scale, operationType, timeShift } = column; const fieldTypes = 'sourceField' in column ? dataView?.getFieldByName(column.sourceField)?.esTypes : undefined; return { @@ -97,6 +101,7 @@ export function columnToOperation( column.dataType === 'string' && fieldTypes?.includes(ES_FIELD_TYPES.VERSION) ? 'version' : undefined, + hasTimeShift: Boolean(timeShift), }; } @@ -451,18 +456,35 @@ export function getIndexPatternDatasource({ getPublicAPI({ state, layerId }: PublicAPIProps) { const columnLabelMap = indexPatternDatasource.uniqueLabels(state); + const layer = state.layers[layerId]; + const visibleColumnIds = layer.columnOrder.filter((colId) => !isReferenced(layer, colId)); return { datasourceId: 'indexpattern', - getTableSpec: () => { - return state.layers[layerId].columnOrder - .filter((colId) => !isReferenced(state.layers[layerId], colId)) - .map((colId) => ({ columnId: colId })); + // consider also referenced columns in this case + // but map fields to the top referencing column + const fieldsPerColumn: Record = {}; + Object.keys(layer.columns).forEach((colId) => { + const visibleColumnId = getReferenceRoot(layer, colId); + fieldsPerColumn[visibleColumnId] = fieldsPerColumn[visibleColumnId] || []; + + const column = layer.columns[colId]; + if (isColumnOfType('terms', column)) { + fieldsPerColumn[visibleColumnId].push( + ...[column.sourceField].concat(column.params.secondaryFields ?? []) + ); + } + if ('sourceField' in column && column.sourceField !== DOCUMENT_FIELD_NAME) { + fieldsPerColumn[visibleColumnId].push(column.sourceField); + } + }); + return visibleColumnIds.map((colId, i) => ({ + columnId: colId, + fields: [...new Set(fieldsPerColumn[colId] || [])], + })); }, getOperationForColumnId: (columnId: string) => { - const layer = state.layers[layerId]; - if (layer && layer.columns[columnId]) { if (!isReferenced(layer, columnId)) { return columnToOperation( @@ -474,10 +496,10 @@ export function getIndexPatternDatasource({ } return null; }, - getVisualDefaults: () => { - const layer = state.layers[layerId]; - return getVisualDefaultsForLayer(layer); - }, + getSourceId: () => layer.indexPatternId, + getFilters: (activeData: FramePublicAPI['activeData']) => + getFiltersInLayer(layer, visibleColumnIds, activeData?.[layerId]), + getVisualDefaults: () => getVisualDefaultsForLayer(layer), }; }, getDatasourceSuggestionsForField(state, draggedField, filterLayers) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 72639f3582583..8c8136371b189 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -1189,6 +1189,7 @@ describe('IndexPattern Data Source suggestions', () => { label: '', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, { @@ -1199,6 +1200,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'Count of records', scale: 'ratio', isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -1276,6 +1278,7 @@ describe('IndexPattern Data Source suggestions', () => { label: '', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, { @@ -1286,6 +1289,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'Count of records', scale: 'ratio', isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -1977,6 +1981,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -2000,6 +2005,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -2047,6 +2053,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, scale: 'interval', isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2057,6 +2064,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: false, scale: 'ratio', isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -2118,6 +2126,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, scale: 'ordinal', isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2128,6 +2137,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, scale: 'interval', isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2138,6 +2148,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: false, scale: 'ratio', isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -2218,6 +2229,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, scale: 'ordinal', isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2228,6 +2240,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, scale: 'interval', isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2238,6 +2251,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: false, scale: 'ratio', isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -2341,6 +2355,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'My Custom Range', scale: 'ordinal', isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2351,6 +2366,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'timestampLabel', scale: 'interval', isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2361,6 +2377,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'Unique count of dest', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -2873,6 +2890,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'My Op', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2883,6 +2901,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'Top 5', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -2947,6 +2966,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'timestampLabel', scale: 'interval', isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2957,6 +2977,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'Cumulative sum of Records label', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, { @@ -2967,6 +2988,7 @@ describe('IndexPattern Data Source suggestions', () => { label: 'Cumulative sum of (incomplete)', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, ], @@ -3029,6 +3051,7 @@ describe('IndexPattern Data Source suggestions', () => { label: '', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, { @@ -3039,6 +3062,7 @@ describe('IndexPattern Data Source suggestions', () => { label: '', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, { @@ -3049,6 +3073,7 @@ describe('IndexPattern Data Source suggestions', () => { label: '', scale: undefined, isStaticValue: false, + hasTimeShift: false, }, }, ], diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index 6f2a2acf3edf0..e30b3bbe8c0b5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -677,6 +677,7 @@ export const termsOperation: OperationDefinition { expect(hasTermsWithManyBuckets(layer)).toBeTruthy(); }); }); + + describe('isReferenced', () => { + it('should return false for top column which has references', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: [], + columns: { + col1: { + operationType: 'managedReference', + references: ['col2'], + label: '', + dataType: 'number', + isBucketed: false, + }, + col2: { + operationType: 'testReference', + references: [], + label: '', + dataType: 'number', + isBucketed: false, + }, + }, + }; + expect(isReferenced(layer, 'col1')).toBeFalsy(); + }); + + it('should return true for referenced column', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: [], + columns: { + col1: { + operationType: 'managedReference', + references: ['col2'], + label: '', + dataType: 'number', + isBucketed: false, + }, + col2: { + operationType: 'testReference', + references: [], + label: '', + dataType: 'number', + isBucketed: false, + }, + }, + }; + expect(isReferenced(layer, 'col2')).toBeTruthy(); + }); + }); + + describe('getReferenceRoot', () => { + it("should just return the column id itself if it's not a referenced column", () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: [], + columns: { + col1: { + operationType: 'managedReference', + references: ['col2'], + label: '', + dataType: 'number', + isBucketed: false, + }, + col2: { + operationType: 'testReference', + references: [], + label: '', + dataType: 'number', + isBucketed: false, + }, + }, + }; + expect(getReferenceRoot(layer, 'col1')).toEqual('col1'); + }); + + it('should return the top column if a referenced column is passed', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: [], + columns: { + col1: { + operationType: 'managedReference', + references: ['col2'], + label: '', + dataType: 'number', + isBucketed: false, + }, + col2: { + operationType: 'testReference', + references: [], + label: '', + dataType: 'number', + isBucketed: false, + }, + }, + }; + expect(getReferenceRoot(layer, 'col2')).toEqual('col1'); + }); + + it('should work for a formula chain', () => { + const math = { + customLabel: true, + dataType: 'number' as const, + isBucketed: false, + label: 'math', + operationType: 'math' as const, + }; + const layer: IndexPatternLayer = { + indexPatternId: '', + columnOrder: [], + columns: { + source: { + dataType: 'number' as const, + isBucketed: false, + label: 'Formula', + operationType: 'formula' as const, + params: { + formula: 'moving_average(sum(bytes), window=5)', + isFormulaBroken: false, + }, + references: ['formulaX3'], + } as FormulaIndexPatternColumn, + formulaX0: { + customLabel: true, + dataType: 'number' as const, + isBucketed: false, + label: 'formulaX0', + operationType: 'sum' as const, + scale: 'ratio' as const, + sourceField: 'bytes', + }, + formulaX1: { + ...math, + label: 'formulaX1', + references: ['formulaX0'], + params: { tinymathAst: 'formulaX0' }, + } as MathIndexPatternColumn, + formulaX2: { + customLabel: true, + dataType: 'number' as const, + isBucketed: false, + label: 'formulaX2', + operationType: 'moving_average' as const, + params: { window: 5 }, + references: ['formulaX1'], + } as MovingAverageIndexPatternColumn, + formulaX3: { + ...math, + label: 'formulaX3', + references: ['formulaX2'], + params: { tinymathAst: 'formulaX2' }, + } as MathIndexPatternColumn, + }, + }; + expect(getReferenceRoot(layer, 'formulaX0')).toEqual('source'); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 5f51b53123170..2252c5b38a541 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -8,6 +8,7 @@ import { partition, mapValues, pickBy, isArray } from 'lodash'; import { CoreStart } from 'kibana/public'; import { Query } from 'src/plugins/data/common'; +import memoizeOne from 'memoize-one'; import type { VisualizeEditorLayersContext } from '../../../../../../src/plugins/visualizations/public'; import type { DatasourceFixAction, @@ -1413,6 +1414,35 @@ export function isReferenced(layer: IndexPatternLayer, columnId: string): boolea return allReferences.includes(columnId); } +const computeReferenceLookup = memoizeOne((layer: IndexPatternLayer): Record => { + // speed up things for deep chains as in formula + const refLookup: Record = {}; + for (const [parentId, col] of Object.entries(layer.columns)) { + if ('references' in col) { + for (const colId of col.references) { + refLookup[colId] = parentId; + } + } + } + return refLookup; +}); + +/** + * Given a columnId, returns the visible root column id for it + * This is useful to map internal properties of referenced columns to the visible column + * @param layer + * @param columnId + * @returns id of the reference root + */ +export function getReferenceRoot(layer: IndexPatternLayer, columnId: string): string { + const refLookup = computeReferenceLookup(layer); + let currentId = columnId; + while (isReferenced(layer, currentId)) { + currentId = refLookup[currentId]; + } + return currentId; +} + export function getReferencedColumnIds(layer: IndexPatternLayer, columnId: string): string[] { const referencedIds: string[] = []; function collect(id: string) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/query_input.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/query_input.tsx index 1b418ee3b408f..2379ca8808beb 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/query_input.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/query_input.tsx @@ -18,6 +18,7 @@ export const QueryInput = ({ isInvalid, onSubmit, disableAutoFocus, + ['data-test-subj']: dataTestSubj, }: { value: Query; onChange: (input: Query) => void; @@ -25,12 +26,13 @@ export const QueryInput = ({ isInvalid: boolean; onSubmit: () => void; disableAutoFocus?: boolean; + 'data-test-subj'?: string; }) => { const { inputValue, handleInputChange } = useDebouncedValue({ value, onChange }); return ( input).filter(({ query }) => query?.trim() && query !== '*'); +} + +/** + * Given an Interval column in range mode transform the ranges into KQL queries + */ +function extractQueriesFromRanges(column: RangeIndexPatternColumn) { + return column.params.ranges + .map(({ from, to }) => { + let rangeQuery = ''; + if (from != null && isFinite(from)) { + rangeQuery += `${column.sourceField} >= ${from}`; + } + if (to != null && isFinite(to)) { + if (rangeQuery.length) { + rangeQuery += ' AND '; + } + rangeQuery += `${column.sourceField} <= ${to}`; + } + return { + query: rangeQuery, + language: 'kuery', + }; + }) + .filter(({ query }) => query?.trim()); +} + +/** + * Given an Terms/Top values column transform each entry into a "field: term" KQL query + * This works also for multi-terms variant + */ +function extractQueriesFromTerms( + column: TermsIndexPatternColumn, + colId: string, + data: NonNullable[string] +): Query[] { + const fields = [column.sourceField] + .concat(column.params.secondaryFields || []) + .filter(Boolean) as string[]; + + // extract the filters from the columns of the activeData + const queries = data.rows + .map(({ [colId]: value }) => { + if (value == null) { + return; + } + if (typeof value !== 'string' && Array.isArray(value.keys)) { + return value.keys + .map( + (term: string, index: number) => + `${fields[index]}: ${`"${term === '' ? escape(term) : term}"`}` + ) + .join(' AND '); + } + return `${column.sourceField}: ${`"${value === '' ? escape(value) : value}"`}`; + }) + .filter(Boolean) as string[]; + + // dedup queries before returning + return [...new Set(queries)].map((query) => ({ language: 'kuery', query })); +} + +/** + * Used for a Terms column to decide whether to use a simple existence query (fallback) instead + * of more specific queries. + * The check targets the scenarios where no data is available, or when there's a transposed table + * and it's not yet possible to track it back to the original table + */ +function shouldUseTermsFallback( + data: NonNullable[string] | undefined, + colId: string +) { + const dataId = data?.columns.find(({ id }) => getOriginalId(id) === colId)?.id; + return !dataId || dataId !== colId; +} + +/** + * Collect filters from metrics: + * * if there's at least one unfiltered metric, then just return an empty list of filters + * * otherwise get all the filters, with the only exception of those from formula (referenced columns will have it anyway) + */ +function collectFiltersFromMetrics(layer: IndexPatternLayer, columnIds: string[]) { + // Isolate filtered metrics first + // mind to ignore non-filterable columns and formula columns + const metricColumns = Object.keys(layer.columns).filter((colId) => { + const column = layer.columns[colId]; + const operationDefinition = operationDefinitionMap[column?.operationType]; + return ( + !column?.isBucketed && + // global filters for formulas are picked up by referenced columns + !isColumnOfType('formula', column) && + operationDefinition?.filterable + ); + }); + const { filtered = [], unfiltered = [] } = groupBy(metricColumns, (colId) => + layer.columns[colId]?.filter ? 'filtered' : 'unfiltered' + ); + + // extract filters from filtered metrics + // consider all the columns, included referenced ones to cover also the formula case + return ( + filtered + // if there are metric columns not filtered, then ignore filtered columns completely + .filter(() => !unfiltered.length) + .map((colId) => layer.columns[colId]?.filter) + // filter out empty filters as well + .filter((filter) => filter?.query?.trim()) as Query[] + ); +} + +interface GroupedQueries { + kuery?: Query[]; + lucene?: Query[]; +} + +function collectOnlyValidQueries( + filteredQueries: GroupedQueries, + operationQueries: GroupedQueries[], + queryLanguage: 'kuery' | 'lucene' +) { + return [ + filteredQueries[queryLanguage], + ...operationQueries.map(({ [queryLanguage]: filter }) => filter), + ].filter((filters) => filters?.length) as Query[][]; +} + +export function getFiltersInLayer( + layer: IndexPatternLayer, + columnIds: string[], + layerData: NonNullable[string] | undefined +) { + const filtersFromMetricsByLanguage = groupBy( + collectFiltersFromMetrics(layer, columnIds), + 'language' + ) as unknown as GroupedQueries; + + const filterOperation = columnIds + .map((colId) => { + const column = layer.columns[colId]; + + if (isColumnOfType('filters', column)) { + const groupsByLanguage = groupBy( + column.params.filters, + ({ input }) => input.language + ) as Record<'lucene' | 'kuery', FiltersIndexPatternColumn['params']['filters']>; + + return { + kuery: extractQueriesFromFilters(groupsByLanguage.kuery), + lucene: extractQueriesFromFilters(groupsByLanguage.lucene), + }; + } + + if (isColumnOfType('range', column) && column.sourceField) { + return { + kuery: extractQueriesFromRanges(column), + }; + } + + if ( + isColumnOfType('terms', column) && + !(column.params.otherBucket || column.params.missingBucket) + ) { + if (!layerData || shouldUseTermsFallback(layerData, colId)) { + const fields = operationDefinitionMap[column.operationType]!.getCurrentFields!(column); + return { + kuery: fields.map((field) => ({ + query: `${field}: *`, + language: 'kuery', + })), + }; + } + + return { + kuery: extractQueriesFromTerms(column, colId, layerData), + }; + } + }) + .filter(Boolean) as GroupedQueries[]; + return { + kuery: collectOnlyValidQueries(filtersFromMetricsByLanguage, filterOperation, 'kuery'), + lucene: collectOnlyValidQueries(filtersFromMetricsByLanguage, filterOperation, 'lucene'), + }; +} diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts index fedd58f3b0807..83a54e4f1a3cd 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts @@ -269,6 +269,8 @@ describe('metric_visualization', () => { dataType: 'number', isBucketed: false, label: 'shazm', + isStaticValue: false, + hasTimeShift: false, }; }, }; diff --git a/x-pack/plugins/lens/public/mocks/datasource_mock.ts b/x-pack/plugins/lens/public/mocks/datasource_mock.ts index 67b286b2ef8a2..c30b39476b1ab 100644 --- a/x-pack/plugins/lens/public/mocks/datasource_mock.ts +++ b/x-pack/plugins/lens/public/mocks/datasource_mock.ts @@ -17,6 +17,8 @@ export function createMockDatasource(id: string): DatasourceMock { getTableSpec: jest.fn(() => []), getOperationForColumnId: jest.fn(), getVisualDefaults: jest.fn(), + getSourceId: jest.fn(), + getFilters: jest.fn(), }; return { diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts index 9ae9f4ac0cae4..fb143bc058e62 100644 --- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts @@ -254,7 +254,10 @@ function expressionHelper( const groups = getSortedGroups(datasource, layer); const operations = groups - .map((columnId) => ({ columnId, operation: datasource.getOperationForColumnId(columnId) })) + .map((columnId) => ({ + columnId, + operation: datasource.getOperationForColumnId(columnId) as Operation | null, + })) .filter((o): o is { columnId: string; operation: Operation } => !!o.operation); if (!layer.metric || !operations.length) { diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 42e4a55167c8b..cfd0f106fae1c 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -93,6 +93,7 @@ import type { SaveModalContainerProps } from './app_plugin/save_modal_container' import { createStartServicesGetter } from '../../../../src/plugins/kibana_utils/public'; import { setupExpressions } from './expressions'; import { getSearchProvider } from './search_provider'; +import type { DiscoverSetup, DiscoverStart } from '../../../../src/plugins/discover/public'; export interface LensPluginSetupDependencies { urlForwarding: UrlForwardingSetup; @@ -104,6 +105,7 @@ export interface LensPluginSetupDependencies { charts: ChartsPluginSetup; globalSearch?: GlobalSearchPluginSetup; usageCollection?: UsageCollectionSetup; + discover?: DiscoverSetup; } export interface LensPluginStartDependencies { @@ -122,6 +124,7 @@ export interface LensPluginStartDependencies { inspector: InspectorStartContract; spaces: SpacesPluginStart; usageCollection?: UsageCollectionStart; + discover?: DiscoverStart; } export interface LensPublicSetup { @@ -248,7 +251,6 @@ export class LensPlugin { fieldFormats, plugins.fieldFormats.deserialize ); - const visualizationMap = await this.editorFrameService!.loadVisualizations(); return { @@ -287,10 +289,10 @@ export class LensPlugin { const getPresentationUtilContext = () => startServices().plugins.presentationUtil.ContextProvider; - const ensureDefaultDataView = async () => { + const ensureDefaultDataView = () => { // make sure a default index pattern exists // if not, the page will be redirected to management and visualize won't be rendered - await startServices().plugins.data.indexPatterns.ensureDefaultDataView(); + startServices().plugins.data.indexPatterns.ensureDefaultDataView(); }; core.application.register({ @@ -300,22 +302,24 @@ export class LensPlugin { mount: async (params: AppMountParameters) => { const { core: coreStart, plugins: deps } = startServices(); - await this.initParts( - core, - data, - charts, - expressions, - fieldFormats, - deps.fieldFormats.deserialize - ); + await Promise.all([ + this.initParts( + core, + data, + charts, + expressions, + fieldFormats, + deps.fieldFormats.deserialize + ), + ensureDefaultDataView(), + ]); const { mountApp, stopReportManager, getLensAttributeService } = await import( './async_services' ); - const frameStart = this.editorFrameService!.start(coreStart, deps); - this.stopReportManager = stopReportManager; - await ensureDefaultDataView(); + + const frameStart = this.editorFrameService!.start(coreStart, deps); return mountApp(core, params, { createEditorFrame: frameStart.createInstance, attributeService: getLensAttributeService(coreStart, deps), diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 7047201c5dba3..1895d26ea89f5 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -355,12 +355,23 @@ export interface DatasourceFixAction { */ export interface DatasourcePublicAPI { datasourceId: string; - getTableSpec: () => Array<{ columnId: string }>; - getOperationForColumnId: (columnId: string) => Operation | null; + getTableSpec: () => Array<{ columnId: string; fields: string[] }>; + getOperationForColumnId: (columnId: string) => OperationDescriptor | null; /** * Collect all default visual values given the current state */ getVisualDefaults: () => Record>; + /** + * Retrieve the specific source id for the current state + */ + getSourceId: () => string | undefined; + /** + * Collect all defined filters from all the operations in the layer + */ + getFilters: (activeData?: FramePublicAPI['activeData']) => { + kuery: Query[][]; + lucene: Query[][]; + }; } export interface DatasourceDataPanelProps { @@ -498,10 +509,17 @@ export interface OperationMetadata { // TODO currently it's not possible to differentiate between a field from a raw // document and an aggregated metric which might be handy in some cases. Once we // introduce a raw document datasource, this should be considered here. - isStaticValue?: boolean; } +/** + * Specific type used to store some meta information on top of the Operation type + * Rather than populate the Operation type with optional types, it can leverage a super type + */ +export interface OperationDescriptor extends Operation { + hasTimeShift: boolean; +} + export interface VisualizationConfigProps { layerId: string; frame: Pick; diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts index 7c17c8ee140cd..d11c2a4aa6f62 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts @@ -8,7 +8,7 @@ import { getGaugeVisualization, isNumericDynamicMetric, isNumericMetric } from './visualization'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { GROUP_ID } from './constants'; -import type { DatasourcePublicAPI, Operation } from '../../types'; +import type { DatasourcePublicAPI, OperationDescriptor } from '../../types'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { CustomPaletteParams, layerTypes } from '../../../common'; import type { GaugeVisualizationState } from './constants'; @@ -58,7 +58,7 @@ describe('gauge', () => { mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', label: 'MyOperation', - } as Operation); + } as OperationDescriptor); frame.datasourceLayers = { first: mockDatasource.publicAPIMock, @@ -461,7 +461,7 @@ describe('gauge', () => { mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', label: 'MyOperation', - } as Operation); + } as OperationDescriptor); datasourceLayers = { first: mockDatasource.publicAPIMock, }; @@ -532,7 +532,7 @@ describe('gauge', () => { mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', label: 'MyOperation', - } as Operation); + } as OperationDescriptor); frame.datasourceLayers = { first: mockDatasource.publicAPIMock, }; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index c73e6c42b53d2..ac3fdcf30a4ad 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -9,7 +9,7 @@ import { Ast } from '@kbn/interpreter'; import { Position } from '@elastic/charts'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; import { getXyVisualization } from './xy_visualization'; -import { Operation } from '../types'; +import { OperationDescriptor } from '../types'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { layerTypes } from '../../common'; import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; @@ -31,14 +31,14 @@ describe('#toExpression', () => { mockDatasource = createMockDatasource('testDatasource'); mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'd' }, - { columnId: 'a' }, - { columnId: 'b' }, - { columnId: 'c' }, + { columnId: 'd', fields: [] }, + { columnId: 'a', fields: [] }, + { columnId: 'b', fields: [] }, + { columnId: 'c', fields: [] }, ]); mockDatasource.publicAPIMock.getOperationForColumnId.mockImplementation((col) => { - return { label: `col_${col}`, dataType: 'number' } as Operation; + return { label: `col_${col}`, dataType: 'number' } as OperationDescriptor; }); frame.datasourceLayers = { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 5e1748a6dc313..89b496a785d9f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -7,7 +7,7 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; -import { Operation, VisualizeEditorContext, Suggestion } from '../types'; +import { Operation, VisualizeEditorContext, Suggestion, OperationDescriptor } from '../types'; import type { State, XYSuggestion } from './types'; import type { SeriesType, XYDataLayerConfig, XYLayerConfig } from '../../common/expressions'; import { layerTypes } from '../../common'; @@ -246,10 +246,10 @@ describe('xy_visualization', () => { mockDatasource = createMockDatasource('testDatasource'); mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'd' }, - { columnId: 'a' }, - { columnId: 'b' }, - { columnId: 'c' }, + { columnId: 'd', fields: [] }, + { columnId: 'a', fields: [] }, + { columnId: 'b', fields: [] }, + { columnId: 'c', fields: [] }, ]); frame.datasourceLayers = { @@ -365,10 +365,10 @@ describe('xy_visualization', () => { mockDatasource = createMockDatasource('testDatasource'); mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'd' }, - { columnId: 'a' }, - { columnId: 'b' }, - { columnId: 'c' }, + { columnId: 'd', fields: [] }, + { columnId: 'a', fields: [] }, + { columnId: 'b', fields: [] }, + { columnId: 'c', fields: [] }, ]); frame.datasourceLayers = { @@ -601,10 +601,10 @@ describe('xy_visualization', () => { mockDatasource = createMockDatasource('testDatasource'); mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'd' }, - { columnId: 'a' }, - { columnId: 'b' }, - { columnId: 'c' }, + { columnId: 'd', fields: [] }, + { columnId: 'a', fields: [] }, + { columnId: 'b', fields: [] }, + { columnId: 'c', fields: [] }, ]); frame.datasourceLayers = { @@ -658,10 +658,10 @@ describe('xy_visualization', () => { mockDatasource = createMockDatasource('testDatasource'); mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'd' }, - { columnId: 'a' }, - { columnId: 'b' }, - { columnId: 'c' }, + { columnId: 'd', fields: [] }, + { columnId: 'a', fields: [] }, + { columnId: 'b', fields: [] }, + { columnId: 'c', fields: [] }, ]); frame.datasourceLayers = { @@ -1085,6 +1085,8 @@ describe('xy_visualization', () => { isBucketed: true, scale: 'interval', label: 'date_histogram', + isStaticValue: false, + hasTimeShift: false, }; } return null; @@ -1112,6 +1114,8 @@ describe('xy_visualization', () => { isBucketed: true, scale: 'interval', label: 'date_histogram', + isStaticValue: false, + hasTimeShift: false, }; } return null; @@ -1153,6 +1157,8 @@ describe('xy_visualization', () => { isBucketed: true, scale: 'interval', label: 'histogram', + isStaticValue: false, + hasTimeShift: false, }; } return null; @@ -1182,6 +1188,8 @@ describe('xy_visualization', () => { isBucketed: true, scale: 'ordinal', label: 'top values', + isStaticValue: false, + hasTimeShift: false, }; } return null; @@ -1209,6 +1217,8 @@ describe('xy_visualization', () => { isBucketed: true, scale: 'ordinal', label: 'top values', + isStaticValue: false, + hasTimeShift: false, }; } return null; @@ -1440,8 +1450,8 @@ describe('xy_visualization', () => { it('should respect the order of accessors coming from datasource', () => { mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'c' }, - { columnId: 'b' }, + { columnId: 'c', fields: [] }, + { columnId: 'b', fields: [] }, ]); const paletteGetter = jest.spyOn(paletteServiceMock, 'get'); // overrite palette with a palette returning first blue, then green as color @@ -1475,7 +1485,7 @@ describe('xy_visualization', () => { mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ dataType: 'string', label: 'MyOperation', - } as Operation); + } as OperationDescriptor); frame.datasourceLayers = { first: mockDatasource.publicAPIMock, @@ -1725,7 +1735,7 @@ describe('xy_visualization', () => { ? ({ dataType: 'date', scale: 'interval', - } as unknown as Operation) + } as unknown as OperationDescriptor) : null ); datasourceLayers.second.getOperationForColumnId = jest.fn((id: string) => @@ -1733,7 +1743,7 @@ describe('xy_visualization', () => { ? ({ dataType: 'number', scale: 'interval', - } as unknown as Operation) + } as unknown as OperationDescriptor) : null ); expect( @@ -1781,7 +1791,7 @@ describe('xy_visualization', () => { ? ({ dataType: 'date', scale: 'interval', - } as unknown as Operation) + } as unknown as OperationDescriptor) : null ); datasourceLayers.second.getOperationForColumnId = jest.fn((id: string) => @@ -1789,7 +1799,7 @@ describe('xy_visualization', () => { ? ({ dataType: 'string', scale: 'ordinal', - } as unknown as Operation) + } as unknown as OperationDescriptor) : null ); expect( @@ -1835,10 +1845,10 @@ describe('xy_visualization', () => { mockDatasource = createMockDatasource('testDatasource'); mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'd' }, - { columnId: 'a' }, - { columnId: 'b' }, - { columnId: 'c' }, + { columnId: 'd', fields: [] }, + { columnId: 'a', fields: [] }, + { columnId: 'b', fields: [] }, + { columnId: 'c', fields: [] }, ]); frame.datasourceLayers = { diff --git a/x-pack/test/functional/apps/lens/formula.ts b/x-pack/test/functional/apps/lens/formula.ts index 12d7f9cf9036b..fcfec350112c4 100644 --- a/x-pack/test/functional/apps/lens/formula.ts +++ b/x-pack/test/functional/apps/lens/formula.ts @@ -145,7 +145,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.goToTimeRange(); await PageObjects.lens.switchToVisualization('lnsDatatable'); - // Close immediately await PageObjects.lens.configureDimension({ dimension: 'lnsDatatable_metrics > lns-empty-dimension', operation: 'formula', diff --git a/x-pack/test/functional/apps/lens/index.ts b/x-pack/test/functional/apps/lens/index.ts index 3687aab7bfb69..20da3e48fc8ae 100644 --- a/x-pack/test/functional/apps/lens/index.ts +++ b/x-pack/test/functional/apps/lens/index.ts @@ -57,6 +57,7 @@ export default function ({ getService, loadTestFile, getPageObjects }: FtrProvid loadTestFile(require.resolve('./dashboard')); loadTestFile(require.resolve('./multi_terms')); loadTestFile(require.resolve('./epoch_millis')); + loadTestFile(require.resolve('./show_underlying_data')); }); describe('', function () { diff --git a/x-pack/test/functional/apps/lens/show_underlying_data.ts b/x-pack/test/functional/apps/lens/show_underlying_data.ts new file mode 100644 index 0000000000000..d6ae299baceaf --- /dev/null +++ b/x-pack/test/functional/apps/lens/show_underlying_data.ts @@ -0,0 +1,178 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header', 'discover']); + const queryBar = getService('queryBar'); + const filterBar = getService('filterBar'); + const listingTable = getService('listingTable'); + const testSubjects = getService('testSubjects'); + const find = getService('find'); + const browser = getService('browser'); + + describe('show underlying data', () => { + it('should show the open button for a compatible saved visualization', async () => { + await PageObjects.visualize.gotoVisualizationLandingPage(); + await listingTable.searchForItemWithName('lnsXYvis'); + await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.waitForVisualization(); + // expect the button is shown and enabled + + await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + + const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); + await browser.switchToWindow(discoverWindowHandle); + + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('discoverChart'); + // check the table columns + const columns = await PageObjects.discover.getColumnHeaders(); + expect(columns).to.eql(['ip', '@timestamp', 'bytes']); + await browser.closeCurrentWindow(); + await browser.switchToWindow(lensWindowHandler); + }); + + it('should ignore the top values column if other category is enabled', async () => { + // Make the breakdown dimention be ignored + await PageObjects.lens.openDimensionEditor( + 'lnsXY_splitDimensionPanel > lns-dimensionTrigger' + ); + await testSubjects.click('indexPattern-terms-advanced'); + await testSubjects.click('indexPattern-terms-other-bucket'); + + await PageObjects.lens.closeDimensionEditor(); + + await PageObjects.lens.waitForVisualization(); + // expect the button is shown and enabled + + await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + + const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); + await browser.switchToWindow(discoverWindowHandle); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('discoverChart'); + expect(await queryBar.getQueryString()).be.eql(''); + await browser.closeCurrentWindow(); + await browser.switchToWindow(lensWindowHandler); + }); + + it('should show the open button for a compatible saved visualization with a lucene query', async () => { + // Make the breakdown dimention contribute to filters again + await PageObjects.lens.openDimensionEditor( + 'lnsXY_splitDimensionPanel > lns-dimensionTrigger' + ); + await testSubjects.click('indexPattern-terms-advanced'); + await testSubjects.click('indexPattern-terms-other-bucket'); + await PageObjects.lens.closeDimensionEditor(); + + // add a lucene query to the yDimension + await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel > lns-dimensionTrigger'); + await PageObjects.lens.enableFilter(); + // turn off the KQL switch to change the language to lucene + await testSubjects.click('indexPattern-filter-by-input > switchQueryLanguageButton'); + await testSubjects.click('languageToggle'); + await testSubjects.click('indexPattern-filter-by-input > switchQueryLanguageButton'); + // apparently setting a filter requires some time before and after typing to work properly + await PageObjects.common.sleep(1000); + await PageObjects.lens.setFilterBy('memory'); + await PageObjects.common.sleep(1000); + + await PageObjects.lens.closeDimensionEditor(); + + await PageObjects.lens.waitForVisualization(); + + await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + + const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); + await browser.switchToWindow(discoverWindowHandle); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('discoverChart'); + // check the query + expect(await queryBar.getQueryString()).be.eql( + '( ( ip: "220.120.146.16" ) OR ( ip: "152.56.56.106" ) OR ( ip: "111.55.80.52" ) )' + ); + const filterPills = await filterBar.getFiltersLabel(); + expect(filterPills.length).to.be(1); + expect(filterPills[0]).to.be('Lens context (lucene)'); + await browser.closeCurrentWindow(); + await browser.switchToWindow(lensWindowHandler); + }); + + it('should show the underlying data extracting all filters and columns from a formula', async () => { + await PageObjects.lens.removeDimension('lnsXY_yDimensionPanel'); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'formula', + formula: `average(memory, kql=`, + keepOpen: true, + }); + + const input = await find.activeElement(); + await input.type(`bytes > 6000`); + // the tooltip seems to be there as long as the focus is in the query string + await input.pressKeys(browser.keys.RIGHT); + + await PageObjects.lens.closeDimensionEditor(); + + await PageObjects.lens.waitForVisualization(); + // expect the button is shown and enabled + await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + + const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); + await browser.switchToWindow(discoverWindowHandle); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('discoverChart'); + // check the columns + const columns = await PageObjects.discover.getColumnHeaders(); + expect(columns).to.eql(['ip', '@timestamp', 'memory']); + // check the query + expect(await queryBar.getQueryString()).be.eql( + '( ( bytes > 6000 ) AND ( ( ip: "0.53.251.53" ) OR ( ip: "0.108.3.2" ) OR ( ip: "0.209.80.244" ) ) )' + ); + await browser.closeCurrentWindow(); + await browser.switchToWindow(lensWindowHandler); + }); + + it('should extract a filter from a formula global filter', async () => { + await PageObjects.lens.removeDimension('lnsXY_yDimensionPanel'); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'formula', + formula: `count()`, + keepOpen: true, + }); + + await PageObjects.lens.enableFilter(); + // apparently setting a filter requires some time before and after typing to work properly + await PageObjects.common.sleep(1000); + await PageObjects.lens.setFilterBy('bytes > 4000'); + await PageObjects.common.sleep(1000); + + await PageObjects.lens.waitForVisualization(); + // expect the button is shown and enabled + await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); + + const [lensWindowHandler, discoverWindowHandle] = await browser.getAllWindowHandles(); + await browser.switchToWindow(discoverWindowHandle); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('discoverChart'); + + // check the query + expect(await queryBar.getQueryString()).be.eql( + '( ( bytes > 4000 ) AND ( ( ip: "0.53.251.53" ) OR ( ip: "0.108.3.2" ) OR ( ip: "0.209.80.244" ) ) )' + ); + await browser.closeCurrentWindow(); + await browser.switchToWindow(lensWindowHandler); + }); + }); +} From 565454c64e6c577567bd04e8617b9aa66343bae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Wed, 9 Mar 2022 09:44:01 +0100 Subject: [PATCH 094/140] Remove deprecated & unused `HttpServiceSetup.auth` (#127056) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...plugin-core-server.httpservicesetup.auth.md | 18 ------------------ ...bana-plugin-core-server.httpservicesetup.md | 1 - src/core/server/http/http_service.mock.ts | 4 ---- src/core/server/http/types.ts | 8 -------- src/core/server/plugins/plugin_context.ts | 4 ---- src/core/server/server.api.md | 2 -- 6 files changed, 37 deletions(-) delete mode 100644 docs/development/core/server/kibana-plugin-core-server.httpservicesetup.auth.md diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.auth.md b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.auth.md deleted file mode 100644 index da348a2282b1a..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.auth.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) > [auth](./kibana-plugin-core-server.httpservicesetup.auth.md) - -## HttpServiceSetup.auth property - -> Warning: This API is now obsolete. -> -> use [the start contract](./kibana-plugin-core-server.httpservicestart.auth.md) instead. -> - -Auth status. See [HttpAuth](./kibana-plugin-core-server.httpauth.md) - -Signature: - -```typescript -auth: HttpAuth; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md index 81ddeaaaa5a12..f3be1a9130b9c 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md @@ -77,7 +77,6 @@ async (context, request, response) => { | Property | Type | Description | | --- | --- | --- | -| [auth](./kibana-plugin-core-server.httpservicesetup.auth.md) | HttpAuth | Auth status. See [HttpAuth](./kibana-plugin-core-server.httpauth.md) | | [basePath](./kibana-plugin-core-server.httpservicesetup.basepath.md) | IBasePath | Access or manipulate the Kibana base path See [IBasePath](./kibana-plugin-core-server.ibasepath.md). | | [createCookieSessionStorageFactory](./kibana-plugin-core-server.httpservicesetup.createcookiesessionstoragefactory.md) | <T>(cookieOptions: SessionStorageCookieOptions<T>) => Promise<SessionStorageFactory<T>> | Creates cookie based session storage factory [SessionStorageFactory](./kibana-plugin-core-server.sessionstoragefactory.md) | | [createRouter](./kibana-plugin-core-server.httpservicesetup.createrouter.md) | <Context extends RequestHandlerContext = RequestHandlerContext>() => IRouter<Context> | Provides ability to declare a handler function for a particular path and HTTP request method. | diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 14baf9ba9257b..f251d3fb64cab 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -179,10 +179,6 @@ const createSetupContractMock = () => { csp: CspConfig.DEFAULT, createRouter: jest.fn(), registerRouteHandlerContext: jest.fn(), - auth: { - get: internalMock.auth.get, - isAuthenticated: internalMock.auth.isAuthenticated, - }, getServerInfo: internalMock.getServerInfo, }; diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index f12533dba4286..e6d9514107798 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -312,14 +312,6 @@ export interface HttpServiceSetup { */ basePath: IBasePath; - /** - * Auth status. - * See {@link HttpAuth} - * - * @deprecated use {@link HttpServiceStart.auth | the start contract} instead. - */ - auth: HttpAuth; - /** * The CSP config used for Kibana. */ diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 983a12627b7f3..f36acfe00d792 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -181,10 +181,6 @@ export function createPluginSetupContext( registerOnPostAuth: deps.http.registerOnPostAuth, registerOnPreResponse: deps.http.registerOnPreResponse, basePath: deps.http.basePath, - auth: { - get: deps.http.auth.get, - isAuthenticated: deps.http.auth.isAuthenticated, - }, csp: deps.http.csp, getServerInfo: deps.http.getServerInfo, }, diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 65ef524f83949..cdf3b5d0556ba 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1127,8 +1127,6 @@ export interface HttpServicePreboot { // @public export interface HttpServiceSetup { - // @deprecated - auth: HttpAuth; basePath: IBasePath; createCookieSessionStorageFactory: (cookieOptions: SessionStorageCookieOptions) => Promise>; createRouter: () => IRouter; From d1f0d23ad72855afd27769e07aff896be2009e8a Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 9 Mar 2022 09:44:50 +0100 Subject: [PATCH 095/140] remove opacity for fitting line series (#127176) --- src/plugins/vis_types/xy/public/utils/render_all_series.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/vis_types/xy/public/utils/render_all_series.tsx b/src/plugins/vis_types/xy/public/utils/render_all_series.tsx index e2672380c390f..d9e4e9a47c23f 100644 --- a/src/plugins/vis_types/xy/public/utils/render_all_series.tsx +++ b/src/plugins/vis_types/xy/public/utils/render_all_series.tsx @@ -196,6 +196,11 @@ export const renderAllSeries = ( area: { ...(type === ChartType.Line ? { opacity: 0 } : { opacity: fillOpacity }), }, + fit: { + area: { + ...(type === ChartType.Line ? { opacity: 0 } : { opacity: fillOpacity }), + }, + }, line: { strokeWidth, visible: drawLinesBetweenPoints, From eb4cd4a2ffff2fe3e1796b0daa0c54930e6854ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Wed, 9 Mar 2022 09:47:41 +0100 Subject: [PATCH 096/140] Remove deprecated & unused `ElasticsearchServiceStart.legacy` (#127050) --- ...-server.elasticsearchservicesetup.legacy.md | 1 - ...-server.elasticsearchservicestart.legacy.md | 18 ------------------ ...in-core-server.elasticsearchservicestart.md | 1 - .../elasticsearch_service.mock.ts | 6 ------ .../elasticsearch/elasticsearch_service.ts | 3 --- src/core/server/elasticsearch/types.ts | 15 --------------- src/core/server/plugins/plugin_context.ts | 1 - src/core/server/server.api.md | 4 ---- 8 files changed, 49 deletions(-) delete mode 100644 docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.legacy.md diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicesetup.legacy.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicesetup.legacy.md index bcc2f474fa483..03e2be0da7a10 100644 --- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicesetup.legacy.md +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicesetup.legacy.md @@ -6,7 +6,6 @@ > Warning: This API is now obsolete. > -> Use [ElasticsearchServiceStart.legacy](./kibana-plugin-core-server.elasticsearchservicestart.legacy.md) instead. > Signature: diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.legacy.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.legacy.md deleted file mode 100644 index 844ebf3815a99..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.legacy.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchServiceStart](./kibana-plugin-core-server.elasticsearchservicestart.md) > [legacy](./kibana-plugin-core-server.elasticsearchservicestart.legacy.md) - -## ElasticsearchServiceStart.legacy property - -> Warning: This API is now obsolete. -> -> Provided for the backward compatibility. Switch to the new elasticsearch client as soon as https://github.com/elastic/kibana/issues/35508 done. -> - -Signature: - -```typescript -legacy: { - readonly config$: Observable; - }; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.md index 5bd8f9d0a4332..66ff94ee9c80d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.md +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.md @@ -17,5 +17,4 @@ export interface ElasticsearchServiceStart | --- | --- | --- | | [client](./kibana-plugin-core-server.elasticsearchservicestart.client.md) | IClusterClient | A pre-configured [Elasticsearch client](./kibana-plugin-core-server.iclusterclient.md) | | [createClient](./kibana-plugin-core-server.elasticsearchservicestart.createclient.md) | (type: string, clientConfig?: Partial<ElasticsearchClientConfig>) => ICustomClusterClient | Create application specific Elasticsearch cluster API client with customized config. See [IClusterClient](./kibana-plugin-core-server.iclusterclient.md). | -| [legacy](./kibana-plugin-core-server.elasticsearchservicestart.legacy.md) | { readonly config$: Observable<ElasticsearchConfig>; } | | diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index f217dbe35c7e9..3ef44e2690a95 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.mock.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.mock.ts @@ -37,9 +37,6 @@ export type MockedElasticSearchServiceSetup = jest.Mocked< }; export interface MockedElasticSearchServiceStart { - legacy: { - config$: BehaviorSubject; - }; client: ClusterClientMock; createClient: jest.MockedFunction< (name: string, config?: Partial) => CustomClusterClientMock @@ -71,9 +68,6 @@ const createStartContractMock = () => { const startContract: MockedElasticSearchServiceStart = { client: elasticsearchClientMock.createClusterClient(), createClient: jest.fn(), - legacy: { - config$: new BehaviorSubject({} as ElasticsearchConfig), - }, }; startContract.createClient.mockImplementation(() => diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index 642b0ab75eaaf..4ab59d12942e7 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -141,9 +141,6 @@ export class ElasticsearchService return { client: this.client!, createClient: (type, clientConfig) => this.createClusterClient(type, config, clientConfig), - legacy: { - config$: this.config$, - }, }; } diff --git a/src/core/server/elasticsearch/types.ts b/src/core/server/elasticsearch/types.ts index c37e33426f7b9..ad26eb427edbc 100644 --- a/src/core/server/elasticsearch/types.ts +++ b/src/core/server/elasticsearch/types.ts @@ -81,7 +81,6 @@ export interface ElasticsearchServiceSetup { /** * @deprecated - * Use {@link ElasticsearchServiceStart.legacy} instead. */ legacy: { /** @@ -136,20 +135,6 @@ export interface ElasticsearchServiceStart { type: string, clientConfig?: Partial ) => ICustomClusterClient; - - /** - * @deprecated - * Provided for the backward compatibility. - * Switch to the new elasticsearch client as soon as https://github.com/elastic/kibana/issues/35508 done. - * */ - legacy: { - /** - * Provide direct access to the current elasticsearch configuration. - * - * @deprecated this will be removed in a later version. - */ - readonly config$: Observable; - }; } /** diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index f36acfe00d792..56d749b79289f 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -242,7 +242,6 @@ export function createPluginStartContext( elasticsearch: { client: deps.elasticsearch.client, createClient: deps.elasticsearch.createClient, - legacy: deps.elasticsearch.legacy, }, executionContext: deps.executionContext, http: { diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index cdf3b5d0556ba..fcf505a13ddbe 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -959,10 +959,6 @@ export interface ElasticsearchServiceSetup { export interface ElasticsearchServiceStart { readonly client: IClusterClient; readonly createClient: (type: string, clientConfig?: Partial) => ICustomClusterClient; - // @deprecated (undocumented) - legacy: { - readonly config$: Observable; - }; } // @public (undocumented) From 06e453ef78351586d25eba1ba9fa4be601fa28dc Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Wed, 9 Mar 2022 11:22:11 +0200 Subject: [PATCH 097/140] add updatedAt to SimpleSavedObject (#126359) --- ...-public.simplesavedobject._constructor_.md | 4 +- ...na-plugin-core-public.simplesavedobject.md | 3 +- ...core-public.simplesavedobject.updatedat.md | 11 ++++ src/core/public/public.api.md | 4 +- .../saved_objects/simple_saved_object.test.ts | 63 +++++++++++++++++++ .../saved_objects/simple_saved_object.ts | 29 ++++++--- 6 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 docs/development/core/public/kibana-plugin-core-public.simplesavedobject.updatedat.md diff --git a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject._constructor_.md b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject._constructor_.md index f53b6e5292861..412154f7ac2e3 100644 --- a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject._constructor_.md +++ b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `SimpleSavedObject` class Signature: ```typescript -constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, namespaces, }: SavedObjectType); +constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, namespaces, updated_at: updatedAt, }: SavedObjectType); ``` ## Parameters @@ -17,5 +17,5 @@ constructor(client: SavedObjectsClientContract, { id, type, version, attributes, | Parameter | Type | Description | | --- | --- | --- | | client | SavedObjectsClientContract | | -| { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, namespaces, } | SavedObjectType<T> | | +| { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, namespaces, updated\_at: updatedAt, } | SavedObjectType<T> | | diff --git a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.md b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.md index 2aac93f9b5bc1..512fc74d538e3 100644 --- a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.md +++ b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.md @@ -18,7 +18,7 @@ export declare class SimpleSavedObject | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(client, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, namespaces, })](./kibana-plugin-core-public.simplesavedobject._constructor_.md) | | Constructs a new instance of the SimpleSavedObject class | +| [(constructor)(client, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, namespaces, updated\_at: updatedAt, })](./kibana-plugin-core-public.simplesavedobject._constructor_.md) | | Constructs a new instance of the SimpleSavedObject class | ## Properties @@ -33,6 +33,7 @@ export declare class SimpleSavedObject | [namespaces](./kibana-plugin-core-public.simplesavedobject.namespaces.md) | | SavedObjectType<T>\['namespaces'\] | Space(s) that this saved object exists in. This attribute is not used for "global" saved object types which are registered with namespaceType: 'agnostic'. | | [references](./kibana-plugin-core-public.simplesavedobject.references.md) | | SavedObjectType<T>\['references'\] | | | [type](./kibana-plugin-core-public.simplesavedobject.type.md) | | SavedObjectType<T>\['type'\] | | +| [updatedAt](./kibana-plugin-core-public.simplesavedobject.updatedat.md) | | SavedObjectType<T>\['updated\_at'\] | | ## Methods diff --git a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.updatedat.md b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.updatedat.md new file mode 100644 index 0000000000000..80b1f95969934 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.updatedat.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SimpleSavedObject](./kibana-plugin-core-public.simplesavedobject.md) > [updatedAt](./kibana-plugin-core-public.simplesavedobject.updatedat.md) + +## SimpleSavedObject.updatedAt property + +Signature: + +```typescript +updatedAt: SavedObjectType['updated_at']; +``` diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index ed119620ac08b..6145cce3912fd 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -1389,7 +1389,7 @@ export class ScopedHistory implements History_2< // @public export class SimpleSavedObject { - constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, namespaces, }: SavedObject); + constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, namespaces, updated_at: updatedAt, }: SavedObject); // (undocumented) attributes: T; // (undocumented) @@ -1416,6 +1416,8 @@ export class SimpleSavedObject { // (undocumented) type: SavedObject['type']; // (undocumented) + updatedAt: SavedObject['updated_at']; + // (undocumented) _version?: SavedObject['version']; } diff --git a/src/core/public/saved_objects/simple_saved_object.test.ts b/src/core/public/saved_objects/simple_saved_object.test.ts index b9338f4dc38bf..432bc215a5b4c 100644 --- a/src/core/public/saved_objects/simple_saved_object.test.ts +++ b/src/core/public/saved_objects/simple_saved_object.test.ts @@ -45,4 +45,67 @@ describe('SimpleSavedObject', () => { const savedObject = new SimpleSavedObject(client, { version } as SavedObject); expect(savedObject._version).toEqual(version); }); + + it('save() changes updatedAt field on existing SimpleSavedObject with an id', async function () { + const date = new Date(); + const initialDate = date.toISOString(); + date.setDate(date.getDate() + 1); + const secondDate = date.toISOString(); + + const config = { + attributes: {}, + id: 'id', + type: 'type', + }; + + const initialSavedObject = new SimpleSavedObject(client, { + ...config, + updated_at: initialDate, + } as SavedObject); + + const updatedSavedObject = new SimpleSavedObject(client, { + ...config, + updated_at: secondDate, + } as SavedObject); + + (client.update as jest.Mock).mockReturnValue(Promise.resolve(updatedSavedObject)); + + const initialValue = initialSavedObject.updatedAt; + await initialSavedObject.save(); + const updatedValue = updatedSavedObject.updatedAt; + + expect(initialValue).not.toEqual(updatedValue); + expect(initialSavedObject.updatedAt).toEqual(updatedValue); + }); + + it('save() changes updatedAt field on existing SimpleSavedObject without an id', async () => { + const date = new Date(); + const initialDate = date.toISOString(); + date.setDate(date.getDate() + 1); + const secondDate = date.toISOString(); + + const config = { + attributes: {}, + type: 'type', + }; + + const initialSavedObject = new SimpleSavedObject(client, { + ...config, + updated_at: initialDate, + } as SavedObject); + + const updatedSavedObject = new SimpleSavedObject(client, { + ...config, + updated_at: secondDate, + } as SavedObject); + + (client.create as jest.Mock).mockReturnValue(Promise.resolve(updatedSavedObject)); + + const initialValue = initialSavedObject.updatedAt; + await initialSavedObject.save(); + const updatedValue = updatedSavedObject.updatedAt; + + expect(initialValue).not.toEqual(updatedValue); + expect(initialSavedObject.updatedAt).toEqual(updatedValue); + }); }); diff --git a/src/core/public/saved_objects/simple_saved_object.ts b/src/core/public/saved_objects/simple_saved_object.ts index 449d3d7943fca..512c6c7656741 100644 --- a/src/core/public/saved_objects/simple_saved_object.ts +++ b/src/core/public/saved_objects/simple_saved_object.ts @@ -30,6 +30,7 @@ export class SimpleSavedObject { public coreMigrationVersion: SavedObjectType['coreMigrationVersion']; public error: SavedObjectType['error']; public references: SavedObjectType['references']; + public updatedAt: SavedObjectType['updated_at']; /** * Space(s) that this saved object exists in. This attribute is not used for "global" saved object types which are registered with * `namespaceType: 'agnostic'`. @@ -48,6 +49,7 @@ export class SimpleSavedObject { migrationVersion, coreMigrationVersion, namespaces, + updated_at: updatedAt, }: SavedObjectType ) { this.id = id; @@ -58,6 +60,7 @@ export class SimpleSavedObject { this.migrationVersion = migrationVersion; this.coreMigrationVersion = coreMigrationVersion; this.namespaces = namespaces; + this.updatedAt = updatedAt; if (error) { this.error = error; } @@ -77,15 +80,25 @@ export class SimpleSavedObject { public save(): Promise> { if (this.id) { - return this.client.update(this.type, this.id, this.attributes, { - references: this.references, - }); + return this.client + .update(this.type, this.id, this.attributes, { + references: this.references, + }) + .then((sso) => { + this.updatedAt = sso.updatedAt; + return sso; + }); } else { - return this.client.create(this.type, this.attributes, { - migrationVersion: this.migrationVersion, - coreMigrationVersion: this.coreMigrationVersion, - references: this.references, - }); + return this.client + .create(this.type, this.attributes, { + migrationVersion: this.migrationVersion, + coreMigrationVersion: this.coreMigrationVersion, + references: this.references, + }) + .then((sso) => { + this.updatedAt = sso.updatedAt; + return sso; + }); } } From 030816d5d459011d8963927d1a7bf7306c6287e2 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 9 Mar 2022 10:52:06 +0100 Subject: [PATCH 098/140] [Reporting] Capture Kibana stopped error (#127017) * added new error and error code * updated error code of auth error and added a test * added a way to observe Kibana shutdown via ReportingCore * added a test for execute report during Kibana shutdown * observe kibana shutdown while performing job and updated report mocks * hook up reporting plugin to "stop" phase of reporting plugin * rather use code from KibanaShuttingDownError * remove done TODO * fix jest test snapshots that are now failing --- .../reporting/common/errors/errors.test.ts | 16 ++++--- .../plugins/reporting/common/errors/index.ts | 14 ++++-- .../server/config/create_config.test.ts | 10 +++++ x-pack/plugins/reporting/server/core.ts | 10 +++++ .../generate_csv/generate_csv.test.ts | 2 +- .../server/lib/tasks/execute_report.test.ts | 43 +++++++++++++++++++ .../server/lib/tasks/execute_report.ts | 18 +++++++- x-pack/plugins/reporting/server/plugin.ts | 4 ++ .../create_mock_reportingplugin.ts | 10 +++++ 9 files changed, 116 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/reporting/common/errors/errors.test.ts b/x-pack/plugins/reporting/common/errors/errors.test.ts index e0cc86a40d373..37210373f4d68 100644 --- a/x-pack/plugins/reporting/common/errors/errors.test.ts +++ b/x-pack/plugins/reporting/common/errors/errors.test.ts @@ -5,17 +5,23 @@ * 2.0. */ -import { AuthenticationExpiredError } from '.'; +import * as errors from '.'; describe('ReportingError', () => { it('provides error code when stringified', () => { - expect(new AuthenticationExpiredError() + '').toBe( - `ReportingError(code: authentication_expired)` + expect(new errors.AuthenticationExpiredError() + '').toBe( + `ReportingError(code: authentication_expired_error)` ); }); it('provides details if there are any and error code when stringified', () => { - expect(new AuthenticationExpiredError('some details') + '').toBe( - `ReportingError(code: authentication_expired) "some details"` + expect(new errors.AuthenticationExpiredError('some details') + '').toBe( + `ReportingError(code: authentication_expired_error) "some details"` ); }); + it('has the expected code structure', () => { + const { ReportingError: _, ...nonAbstractErrors } = errors; + Object.values(nonAbstractErrors).forEach((Ctor) => { + expect(new Ctor().code).toMatch(/^[a-z_]+_error$/); + }); + }); }); diff --git a/x-pack/plugins/reporting/common/errors/index.ts b/x-pack/plugins/reporting/common/errors/index.ts index 6064eca33ed7b..0e84fa854f386 100644 --- a/x-pack/plugins/reporting/common/errors/index.ts +++ b/x-pack/plugins/reporting/common/errors/index.ts @@ -9,6 +9,12 @@ import { i18n } from '@kbn/i18n'; export abstract class ReportingError extends Error { + /** + * A string that uniquely brands an error type. This is used to power telemetry + * about reporting failures. + * + * @note Convention for codes: lower-case, snake-case and end in `_error`. + */ public abstract code: string; constructor(public details?: string) { @@ -32,7 +38,7 @@ export abstract class ReportingError extends Error { * access token expired. */ export class AuthenticationExpiredError extends ReportingError { - code = 'authentication_expired'; + code = 'authentication_expired_error'; } export class QueueTimeoutError extends ReportingError { @@ -59,7 +65,9 @@ export class PdfWorkerOutOfMemoryError extends ReportingError { } } -// TODO: Add ReportingError for Kibana stopping unexpectedly -// TODO: Add ReportingError for missing Chromium dependencies +export class KibanaShuttingDownError extends ReportingError { + code = 'kibana_shutting_down_error'; +} + // TODO: Add ReportingError for missing Chromium dependencies // TODO: Add ReportingError for Chromium not starting for an unknown reason diff --git a/x-pack/plugins/reporting/server/config/create_config.test.ts b/x-pack/plugins/reporting/server/config/create_config.test.ts index f839d72e1a45d..498281c56424f 100644 --- a/x-pack/plugins/reporting/server/config/create_config.test.ts +++ b/x-pack/plugins/reporting/server/config/create_config.test.ts @@ -76,6 +76,16 @@ describe('Reporting server createConfig$', () => { expect(result).toMatchInlineSnapshot(` Object { + "capture": Object { + "loadDelay": 1, + "maxAttempts": 1, + "timeouts": Object { + "openUrl": 100, + "renderComplete": 100, + "waitForElements": 100, + }, + "zoom": 1, + }, "csv": Object {}, "encryptionKey": "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii", "index": ".reporting", diff --git a/x-pack/plugins/reporting/server/core.ts b/x-pack/plugins/reporting/server/core.ts index a4e4f43f90e1e..ee50b99e5b3b7 100644 --- a/x-pack/plugins/reporting/server/core.ts +++ b/x-pack/plugins/reporting/server/core.ts @@ -82,6 +82,8 @@ export class ReportingCore { public getContract: () => ReportingSetup; + private kibanaShuttingDown$ = new Rx.ReplaySubject(1); + constructor(private logger: Logger, context: PluginInitializerContext) { this.packageInfo = context.env.packageInfo; const syncConfig = context.config.get(); @@ -129,6 +131,14 @@ export class ReportingCore { await Promise.all([executeTask.init(taskManager), monitorTask.init(taskManager)]); } + public pluginStop() { + this.kibanaShuttingDown$.next(); + } + + public getKibanaShutdown$(): Rx.Observable { + return this.kibanaShuttingDown$.pipe(take(1)); + } + private async assertKibanaIsAvailable(): Promise { const { status } = this.getPluginSetupDeps(); diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts index 66ee465ea142f..37ec0d400f473 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts @@ -847,7 +847,7 @@ describe('error codes', () => { ); const { error_code: errorCode, warnings } = await generateCsv.generateData(); - expect(errorCode).toBe('authentication_expired'); + expect(errorCode).toBe('authentication_expired_error'); expect(warnings).toMatchInlineSnapshot(` Array [ "This report contains partial CSV results because the authentication token expired. Export a smaller amount of data or increase the timeout of the authentication token.", diff --git a/x-pack/plugins/reporting/server/lib/tasks/execute_report.test.ts b/x-pack/plugins/reporting/server/lib/tasks/execute_report.test.ts index b47df99b7a0fd..9f016a6a54a10 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/execute_report.test.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/execute_report.test.ts @@ -9,6 +9,8 @@ import { loggingSystemMock } from 'src/core/server/mocks'; import { ReportingCore } from '../..'; import { RunContext } from '../../../../task_manager/server'; import { taskManagerMock } from '../../../../task_manager/server/mocks'; +import { KibanaShuttingDownError } from '../../../common/errors'; +import type { SavedReport } from '../store'; import { ReportingConfigType } from '../../config'; import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers'; import { ExecuteReportTask } from './'; @@ -79,4 +81,45 @@ describe('Execute Report Task', () => { } `); }); + + it('throws during reporting if Kibana starts shutting down', async () => { + mockReporting.getExportTypesRegistry().register({ + id: 'noop', + name: 'Noop', + createJobFnFactory: () => async () => new Promise(() => {}), + runTaskFnFactory: () => async () => new Promise(() => {}), + jobContentExtension: 'none', + jobType: 'noop', + validLicenses: [], + }); + const store = await mockReporting.getStore(); + store.setReportFailed = jest.fn(() => Promise.resolve({} as any)); + const task = new ExecuteReportTask(mockReporting, configType, logger); + task._claimJob = jest.fn(() => + Promise.resolve({ _id: 'test', jobtype: 'noop', status: 'pending' } as SavedReport) + ); + const mockTaskManager = taskManagerMock.createStart(); + await task.init(mockTaskManager); + + const taskDef = task.getTaskDefinition(); + const taskRunner = taskDef.createTaskRunner({ + taskInstance: { + id: 'random-task-id', + params: { index: 'cool-reporting-index', id: 'noop', jobtype: 'noop', payload: {} }, + }, + } as unknown as RunContext); + + const taskPromise = taskRunner.run(); + setImmediate(() => { + mockReporting.pluginStop(); + }); + await taskPromise; + + expect(store.setReportFailed).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + output: expect.objectContaining({ error_code: new KibanaShuttingDownError().code }), + }) + ); + }); }); diff --git a/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts b/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts index 4d4959eef00c4..9007f5701f6f9 100644 --- a/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts +++ b/x-pack/plugins/reporting/server/lib/tasks/execute_report.ts @@ -20,7 +20,12 @@ import type { TaskRunCreatorFunction, } from '../../../../task_manager/server'; import { CancellationToken } from '../../../common/cancellation_token'; -import { QueueTimeoutError, ReportingError, UnknownError } from '../../../common/errors'; +import { + ReportingError, + UnknownError, + QueueTimeoutError, + KibanaShuttingDownError, +} from '../../../common/errors'; import { durationToNumber, numberToDuration } from '../../../common/schema_utils'; import type { ReportOutput } from '../../../common/types'; import type { ReportingConfigType } from '../../config'; @@ -288,6 +293,12 @@ export class ExecuteReportTask implements ReportingTask { return report; } + // Generic is used to let TS infer the return type at call site. + private async throwIfKibanaShutsDown(): Promise { + await this.reporting.getKibanaShutdown$().toPromise(); + throw new KibanaShuttingDownError(); + } + /* * Provides a TaskRunner for Task Manager */ @@ -361,7 +372,10 @@ export class ExecuteReportTask implements ReportingTask { eventLog.logExecutionStart(); - const output = await this._performJob(task, cancellationToken, stream); + const output = await Promise.race([ + this._performJob(task, cancellationToken, stream), + this.throwIfKibanaShutsDown(), + ]); stream.end(); diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index 37d6494f5e079..52a72e7139020 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -113,4 +113,8 @@ export class ReportingPlugin return reportingCore.getContract(); } + + stop() { + this.reportingCore?.pluginStop(); + } } diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts index e00ebd99f0420..414d3dd10f8ff 100644 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts +++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts @@ -119,6 +119,16 @@ export const createMockConfigSchema = ( enabled: false, ...overrides.roles, }, + capture: { + maxAttempts: 1, + loadDelay: 1, + timeouts: { + openUrl: 100, + renderComplete: 100, + waitForElements: 100, + }, + zoom: 1, + }, } as ReportingConfigType; }; From f74c894c5d68a0d6fb9cc8d792116f86d4fdf31c Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Wed, 9 Mar 2022 11:11:34 +0100 Subject: [PATCH 099/140] Extend yarn es: plugin support + adding secure files to es keystore (#126938) Co-authored-by: Spencer --- packages/kbn-es/src/cli_commands/snapshot.js | 10 +++ packages/kbn-es/src/cli_commands/source.js | 10 +++ packages/kbn-es/src/cluster.js | 68 ++++++++++++++++---- packages/kbn-es/src/paths.ts | 1 + 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/packages/kbn-es/src/cli_commands/snapshot.js b/packages/kbn-es/src/cli_commands/snapshot.js index 1c902796a0a0c..095ce3cb04299 100644 --- a/packages/kbn-es/src/cli_commands/snapshot.js +++ b/packages/kbn-es/src/cli_commands/snapshot.js @@ -33,6 +33,8 @@ exports.help = (defaults = {}) => { --use-cached Skips cache verification and use cached ES snapshot. --skip-ready-check Disable the ready check, --ready-timeout Customize the ready check timeout, in seconds or "Xm" format, defaults to 1m + --plugins Comma seperated list of Elasticsearch plugins to install + --secure-files Comma seperated list of secure_setting_name=/path pairs Example: @@ -58,6 +60,7 @@ exports.run = async (defaults = {}) => { useCached: 'use-cached', skipReadyCheck: 'skip-ready-check', readyTimeout: 'ready-timeout', + secureFiles: 'secure-files', }, string: ['version', 'ready-timeout'], @@ -76,6 +79,13 @@ exports.run = async (defaults = {}) => { if (options.dataArchive) { await cluster.extractDataDirectory(installPath, options.dataArchive); } + if (options.plugins) { + await cluster.installPlugins(installPath, options.plugins, options); + } + if (options.secureFiles) { + const pairs = options.secureFiles.split(',').map((kv) => kv.split('=').map((v) => v.trim())); + await cluster.configureKeystoreWithSecureSettingsFiles(installPath, pairs); + } reportTime(installStartTime, 'installed', { success: true, diff --git a/packages/kbn-es/src/cli_commands/source.js b/packages/kbn-es/src/cli_commands/source.js index c16e89e2c7f32..d1f8e02b55680 100644 --- a/packages/kbn-es/src/cli_commands/source.js +++ b/packages/kbn-es/src/cli_commands/source.js @@ -27,6 +27,8 @@ exports.help = (defaults = {}) => { --password Sets password for elastic user [default: ${password}] --password.[user] Sets password for native realm user [default: ${password}] --ssl Sets up SSL on Elasticsearch + --plugins Comma seperated list of Elasticsearch plugins to install + --secure-files Comma seperated list of secure_setting_name=/path pairs -E Additional key=value settings to pass to Elasticsearch --skip-ready-check Disable the ready check, --ready-timeout Customize the ready check timeout, in seconds or "Xm" format, defaults to 1m @@ -47,6 +49,7 @@ exports.run = async (defaults = {}) => { dataArchive: 'data-archive', skipReadyCheck: 'skip-ready-check', readyTimeout: 'ready-timeout', + secureFiles: 'secure-files', esArgs: 'E', }, @@ -62,6 +65,13 @@ exports.run = async (defaults = {}) => { if (options.dataArchive) { await cluster.extractDataDirectory(installPath, options.dataArchive); } + if (options.plugins) { + await cluster.installPlugins(installPath, options.plugins, options); + } + if (options.secureFiles) { + const pairs = options.secureFiles.split(',').map((kv) => kv.split('=').map((v) => v.trim())); + await cluster.configureKeystoreWithSecureSettingsFiles(installPath, pairs); + } await cluster.run(installPath, { ...options, diff --git a/packages/kbn-es/src/cluster.js b/packages/kbn-es/src/cluster.js index 3dd6d79fcb14e..a6faffc2cfcd7 100644 --- a/packages/kbn-es/src/cluster.js +++ b/packages/kbn-es/src/cluster.js @@ -12,7 +12,7 @@ const chalk = require('chalk'); const path = require('path'); const { Client } = require('@elastic/elasticsearch'); const { downloadSnapshot, installSnapshot, installSource, installArchive } = require('./install'); -const { ES_BIN } = require('./paths'); +const { ES_BIN, ES_PLUGIN_BIN, ES_KEYSTORE_BIN } = require('./paths'); const { log: defaultLog, parseEsLog, @@ -150,6 +150,42 @@ exports.Cluster = class Cluster { }); } + /** + * Starts ES and returns resolved promise once started + * + * @param {String} installPath + * @param {String} plugins - comma separated list of plugins to install + * @param {Object} options + * @returns {Promise} + */ + async installPlugins(installPath, plugins, options) { + const esJavaOpts = this.javaOptions(options); + for (const plugin of plugins.split(',')) { + await execa(ES_PLUGIN_BIN, ['install', plugin.trim()], { + cwd: installPath, + env: { + JAVA_HOME: '', // By default, we want to always unset JAVA_HOME so that the bundled JDK will be used + ES_JAVA_OPTS: esJavaOpts.trim(), + }, + }); + } + } + + async configureKeystoreWithSecureSettingsFiles(installPath, secureSettingsFiles) { + const env = { JAVA_HOME: '' }; + for (const [secureSettingName, secureSettingFile] of secureSettingsFiles) { + this._log.info( + `setting secure setting %s to %s`, + chalk.bold(secureSettingName), + chalk.bold(secureSettingFile) + ); + await execa(ES_KEYSTORE_BIN, ['add-file', secureSettingName, secureSettingFile], { + cwd: installPath, + env, + }); + } + } + /** * Starts ES and returns resolved promise once started * @@ -280,19 +316,9 @@ exports.Cluster = class Cluster { ); this._log.info('%s %s', ES_BIN, args.join(' ')); + const esJavaOpts = this.javaOptions(options); - let esJavaOpts = `${options.esJavaOpts || ''} ${process.env.ES_JAVA_OPTS || ''}`; - - // ES now automatically sets heap size to 50% of the machine's available memory - // so we need to set it to a smaller size for local dev and CI - // especially because we currently run many instances of ES on the same machine during CI - // inital and max must be the same, so we only need to check the max - if (!esJavaOpts.includes('Xmx')) { - // 1536m === 1.5g - esJavaOpts += ' -Xms1536m -Xmx1536m'; - } - - this._log.info('ES_JAVA_OPTS: %s', esJavaOpts.trim()); + this._log.info('ES_JAVA_OPTS: %s', esJavaOpts); this._process = execa(ES_BIN, args, { cwd: installPath, @@ -300,7 +326,7 @@ exports.Cluster = class Cluster { ...(installPath ? { ES_TMPDIR: path.resolve(installPath, 'ES_TMPDIR') } : {}), ...process.env, JAVA_HOME: '', // By default, we want to always unset JAVA_HOME so that the bundled JDK will be used - ES_JAVA_OPTS: esJavaOpts.trim(), + ES_JAVA_OPTS: esJavaOpts, }, stdio: ['ignore', 'pipe', 'pipe'], }); @@ -429,4 +455,18 @@ exports.Cluster = class Cluster { } } } + + javaOptions(options) { + let esJavaOpts = `${options.esJavaOpts || ''} ${process.env.ES_JAVA_OPTS || ''}`; + + // ES now automatically sets heap size to 50% of the machine's available memory + // so we need to set it to a smaller size for local dev and CI + // especially because we currently run many instances of ES on the same machine during CI + // inital and max must be the same, so we only need to check the max + if (!esJavaOpts.includes('Xmx')) { + // 1536m === 1.5g + esJavaOpts += ' -Xms1536m -Xmx1536m'; + } + return esJavaOpts.trim(); + } }; diff --git a/packages/kbn-es/src/paths.ts b/packages/kbn-es/src/paths.ts index c1b859af4e1f5..1d909f523302e 100644 --- a/packages/kbn-es/src/paths.ts +++ b/packages/kbn-es/src/paths.ts @@ -19,6 +19,7 @@ export const BASE_PATH = Path.resolve(tempDir, 'kbn-es'); export const GRADLE_BIN = maybeUseBat('./gradlew'); export const ES_BIN = maybeUseBat('bin/elasticsearch'); +export const ES_PLUGIN_BIN = maybeUseBat('bin/elasticsearch-plugin'); export const ES_CONFIG = 'config/elasticsearch.yml'; export const ES_KEYSTORE_BIN = maybeUseBat('./bin/elasticsearch-keystore'); From f5556f132732f070d10277053913fffaafebdedf Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 9 Mar 2022 11:34:47 +0100 Subject: [PATCH 100/140] [Lens] Fix multi terms fields validation (#126618) * :bug: Fix fields validation for multi terms * :bug: Fix unit tests + add some new ones * :globe_with_meridians: Removed unused translations * Revert ":globe_with_meridians: Removed unused translations" This reverts commit a94ee699c0b7b7e5db2f2b3b6a764a454f18510b. * :globe_with_meridians: Removed unused translations * :bug: Extends deeper validation to drag and drop as well + more tests * :bug: Fix last issue with refactored function * :sparkles: Filtered fields with unsupported types when in multi mode Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../droppable/get_drop_props.ts | 2 +- .../operations/definitions/helpers.test.ts | 83 +++++++++- .../operations/definitions/helpers.tsx | 62 +++++--- .../operations/definitions/index.ts | 6 + .../operations/definitions/terms/constants.ts | 14 ++ .../definitions/terms/field_inputs.tsx | 55 +++++-- .../definitions/terms/helpers.test.ts | 2 +- .../operations/definitions/terms/helpers.ts | 68 +++++++- .../operations/definitions/terms/index.tsx | 112 +++++-------- .../definitions/terms/terms.test.tsx | 150 +++++++++++++++++- .../public/indexpattern_datasource/utils.tsx | 3 +- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 13 files changed, 434 insertions(+), 127 deletions(-) create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/constants.ts diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/get_drop_props.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/get_drop_props.ts index d85cbd438ffe7..f3c48bace4a5f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/get_drop_props.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/get_drop_props.ts @@ -134,7 +134,7 @@ function getDropPropsForField({ const isTheSameIndexPattern = state.layers[layerId].indexPatternId === dragging.indexPatternId; const newOperation = getNewOperation(dragging.field, filterOperations, targetColumn); - if (!!(isTheSameIndexPattern && newOperation)) { + if (isTheSameIndexPattern && newOperation) { const nextLabel = operationLabels[newOperation].displayName; if (!targetColumn) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts index bf24e31ad4f59..616c1f5719cc6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts @@ -7,6 +7,7 @@ import { createMockedIndexPattern } from '../../mocks'; import { getInvalidFieldMessage } from './helpers'; +import type { TermsIndexPatternColumn } from './terms'; describe('helpers', () => { describe('getInvalidFieldMessage', () => { @@ -16,13 +17,13 @@ describe('helpers', () => { dataType: 'number', isBucketed: false, label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'bytes', + operationType: 'count', + sourceField: 'NoBytes', // <= invalid }, createMockedIndexPattern() ); expect(messages).toHaveLength(1); - expect(messages![0]).toEqual('Field bytes was not found'); + expect(messages![0]).toEqual('Field NoBytes was not found'); }); it('returns an error if a field is the wrong type', () => { @@ -31,8 +32,8 @@ describe('helpers', () => { dataType: 'number', isBucketed: false, label: 'Foo', - operationType: 'average', // <= invalid - sourceField: 'timestamp', + operationType: 'average', + sourceField: 'timestamp', // <= invalid type for average }, createMockedIndexPattern() ); @@ -40,6 +41,78 @@ describe('helpers', () => { expect(messages![0]).toEqual('Field timestamp is of the wrong type'); }); + it('returns an error if one field amongst multiples does not exist', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'terms', + sourceField: 'geo.src', + params: { + secondaryFields: ['NoBytes'], // <= field does not exist + }, + } as TermsIndexPatternColumn, + createMockedIndexPattern() + ); + expect(messages).toHaveLength(1); + expect(messages![0]).toEqual('Field NoBytes was not found'); + }); + + it('returns an error if multiple fields do not exist', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'terms', + sourceField: 'NotExisting', + params: { + secondaryFields: ['NoBytes'], // <= field does not exist + }, + } as TermsIndexPatternColumn, + createMockedIndexPattern() + ); + expect(messages).toHaveLength(1); + expect(messages![0]).toEqual('Fields NotExisting, NoBytes were not found'); + }); + + it('returns an error if one field amongst multiples has the wrong type', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'terms', + sourceField: 'geo.src', + params: { + secondaryFields: ['timestamp'], // <= invalid type + }, + } as TermsIndexPatternColumn, + createMockedIndexPattern() + ); + expect(messages).toHaveLength(1); + expect(messages![0]).toEqual('Field timestamp is of the wrong type'); + }); + + it('returns an error if multiple fields are of the wrong type', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'terms', + sourceField: 'start_date', // <= invalid type + params: { + secondaryFields: ['timestamp'], // <= invalid type + }, + } as TermsIndexPatternColumn, + createMockedIndexPattern() + ); + expect(messages).toHaveLength(1); + expect(messages![0]).toEqual('Fields start_date, timestamp are of the wrong type'); + }); + it('returns no message if all fields are matching', () => { const messages = getInvalidFieldMessage( { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx index 73e0e61a68950..c464ce0da027c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx @@ -12,7 +12,8 @@ import { FormattedIndexPatternColumn, ReferenceBasedIndexPatternColumn, } from './column_types'; -import { IndexPattern } from '../../types'; +import { IndexPattern, IndexPatternField } from '../../types'; +import { hasField } from '../../pure_utils'; export function getInvalidFieldMessage( column: FieldBasedIndexPatternColumn, @@ -21,47 +22,66 @@ export function getInvalidFieldMessage( if (!indexPattern) { return; } - const { sourceField, operationType } = column; - const field = sourceField ? indexPattern.getFieldByName(sourceField) : undefined; - const operationDefinition = operationType && operationDefinitionMap[operationType]; + const { operationType } = column; + const operationDefinition = operationType ? operationDefinitionMap[operationType] : undefined; + const fieldNames = + hasField(column) && operationDefinition + ? operationDefinition?.getCurrentFields?.(column) ?? [column.sourceField] + : []; + const fields = fieldNames.map((fieldName) => indexPattern.getFieldByName(fieldName)); + const filteredFields = fields.filter(Boolean) as IndexPatternField[]; const isInvalid = Boolean( - sourceField && - operationDefinition && + fields.length > filteredFields.length || !( - field && operationDefinition?.input === 'field' && - operationDefinition.getPossibleOperationForField(field) !== undefined + filteredFields.every( + (field) => operationDefinition.getPossibleOperationForField(field) != null + ) ) ); const isWrongType = Boolean( - sourceField && - operationDefinition && - field && - !operationDefinition.isTransferable( + filteredFields.length && + !operationDefinition?.isTransferable( column as GenericIndexPatternColumn, indexPattern, operationDefinitionMap ) ); + if (isInvalid) { + // Missing fields have priority over wrong type + // This has been moved as some transferable checks also perform exist checks internally and fail eventually + // but that would make type mismatch error appear in place of missing fields scenarios + const missingFields = fields.map((field, i) => (field ? null : fieldNames[i])).filter(Boolean); + if (missingFields.length) { + return [ + i18n.translate('xpack.lens.indexPattern.fieldsNotFound', { + defaultMessage: + '{count, plural, one {Field} other {Fields}} {missingFields} {count, plural, one {was} other {were}} not found', + values: { + count: missingFields.length, + missingFields: missingFields.join(', '), + }, + }), + ]; + } if (isWrongType) { + // as fallback show all the fields as invalid? + const wrongTypeFields = + operationDefinition?.getNonTransferableFields?.(column, indexPattern) ?? fieldNames; return [ - i18n.translate('xpack.lens.indexPattern.fieldWrongType', { - defaultMessage: 'Field {invalidField} is of the wrong type', + i18n.translate('xpack.lens.indexPattern.fieldsWrongType', { + defaultMessage: + '{count, plural, one {Field} other {Fields}} {invalidFields} {count, plural, one {is} other {are}} of the wrong type', values: { - invalidField: sourceField, + count: wrongTypeFields.length, + invalidFields: wrongTypeFields.join(', '), }, }), ]; } - return [ - i18n.translate('xpack.lens.indexPattern.fieldNotFound', { - defaultMessage: 'Field {invalidField} was not found', - values: { invalidField: sourceField }, - }), - ]; } return undefined; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index a048f2b559191..dec70130d1282 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -336,6 +336,12 @@ interface BaseOperationDefinitionProps * Operation can influence some visual default settings. This function is used to collect default values offered */ getDefaultVisualSettings?: (column: C) => { truncateText?: boolean }; + + /** + * Utility function useful for multi fields operation in order to get fields + * are not pass the transferable checks + */ + getNonTransferableFields?: (column: C, indexPattern: IndexPattern) => string[]; } interface BaseBuildColumnArgs { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/constants.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/constants.ts new file mode 100644 index 0000000000000..64967aab08733 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/constants.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const DEFAULT_SIZE = 3; +// Elasticsearch limit +export const MAXIMUM_MAX_DOC_COUNT = 100; +export const DEFAULT_MAX_DOC_COUNT = 1; +export const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']); + +export const MULTI_KEY_VISUAL_SEPARATOR = '›'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx index e187b12f323c6..b21bfdc67fe23 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/field_inputs.tsx @@ -21,6 +21,7 @@ import { FieldSelect } from '../../../dimension_panel/field_select'; import type { TermsIndexPatternColumn } from './types'; import type { IndexPattern, IndexPatternPrivateState } from '../../../types'; import type { OperationSupportMatrix } from '../../../dimension_panel'; +import { supportedTypes } from './constants'; const generateId = htmlIdGenerator(); export const MAX_MULTI_FIELDS_SIZE = 3; @@ -29,6 +30,7 @@ export interface FieldInputsProps { column: TermsIndexPatternColumn; indexPattern: IndexPattern; existingFields: IndexPatternPrivateState['existingFields']; + invalidFields?: string[]; operationSupportMatrix: Pick; onChange: (newValues: string[]) => void; } @@ -51,6 +53,7 @@ export function FieldInputs({ indexPattern, existingFields, operationSupportMatrix, + invalidFields, }: FieldInputsProps) { const onChangeWrapped = useCallback( (values: WrappedValue[]) => @@ -90,7 +93,7 @@ export function FieldInputs({ return ( <> { // need to filter the available fields for multiple terms // * a scripted field should be removed - // * if a field has been used, should it be removed? Probably yes? - // * if a scripted field was used in a singular term, should it be marked as invalid for multi-terms? Probably yes? + // * a field of unsupported type should be removed + // * a field that has been used + // * a scripted field was used in a singular term, should be marked as invalid for multi-terms const filteredOperationByField = Object.keys(operationSupportMatrix.operationByField) - .filter( - (key) => - (!rawValuesLookup.has(key) && !indexPattern.getFieldByName(key)?.scripted) || - key === value - ) + .filter((key) => { + if (key === value) { + return true; + } + const field = indexPattern.getFieldByName(key); + return ( + !rawValuesLookup.has(key) && + field && + !field.scripted && + supportedTypes.has(field.type) + ); + }) .reduce((memo, key) => { memo[key] = operationSupportMatrix.operationByField[key]; return memo; }, {}); - const shouldShowScriptedFieldError = Boolean( - value && indexPattern.getFieldByName(value)?.scripted && localValuesFilled.length > 1 + const shouldShowError = Boolean( + value && + ((indexPattern.getFieldByName(value)?.scripted && localValuesFilled.length > 1) || + invalidFields?.includes(value)) ); return ( { onFieldSelectChange(choice, index); }} - isInvalid={shouldShowScriptedFieldError} + isInvalid={shouldShowError} data-test-subj={`indexPattern-dimension-field-${index}`} /> @@ -233,3 +246,21 @@ export function FieldInputs({ ); } + +export function getInputFieldErrorMessage(isScriptedField: boolean, invalidFields: string[]) { + if (isScriptedField) { + return i18n.translate('xpack.lens.indexPattern.terms.scriptedFieldErrorShort', { + defaultMessage: 'Scripted fields are not supported when using multiple fields', + }); + } + if (invalidFields.length) { + return i18n.translate('xpack.lens.indexPattern.terms.invalidFieldsErrorShort', { + defaultMessage: + 'Invalid {invalidFieldsCount, plural, one {field} other {fields}}: {invalidFields}. Check your data view or pick another field.', + values: { + invalidFieldsCount: invalidFields.length, + invalidFields: invalidFields.map((fieldName) => `"${fieldName}"`).join(', '), + }, + }); + } +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.test.ts index 628a5d6a7740a..cdc0f92121f06 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.test.ts @@ -15,9 +15,9 @@ import { getDisallowedTermsMessage, getMultiTermsScriptedFieldErrorMessage, isSortableByColumn, - MULTI_KEY_VISUAL_SEPARATOR, } from './helpers'; import { ReferenceBasedIndexPatternColumn } from '../column_types'; +import { MULTI_KEY_VISUAL_SEPARATOR } from './constants'; const indexPattern = createMockedIndexPattern(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts index 2917abbf848f8..49e612f8bb0d2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts @@ -10,7 +10,7 @@ import { uniq } from 'lodash'; import type { CoreStart } from 'kibana/public'; import { buildEsQuery } from '@kbn/es-query'; import { getEsQueryConfig } from '../../../../../../../../src/plugins/data/public'; -import { operationDefinitionMap } from '../index'; +import { GenericIndexPatternColumn, operationDefinitionMap } from '../index'; import { defaultLabel } from '../filters'; import { isReferenced } from '../../layer_helpers'; @@ -18,9 +18,9 @@ import type { FieldStatsResponse } from '../../../../../common'; import type { FrameDatasourceAPI } from '../../../../types'; import type { FiltersIndexPatternColumn } from '../index'; import type { TermsIndexPatternColumn } from './types'; -import type { IndexPatternLayer, IndexPattern } from '../../../types'; - -export const MULTI_KEY_VISUAL_SEPARATOR = '›'; +import type { IndexPatternLayer, IndexPattern, IndexPatternField } from '../../../types'; +import { MULTI_KEY_VISUAL_SEPARATOR, supportedTypes } from './constants'; +import { isColumnOfType } from '../helpers'; const fullSeparatorString = ` ${MULTI_KEY_VISUAL_SEPARATOR} `; @@ -213,3 +213,63 @@ export function isSortableByColumn(layer: IndexPatternLayer, columnId: string) { !isReferenced(layer, columnId) ); } + +export function isScriptedField(field: IndexPatternField): boolean; +export function isScriptedField(fieldName: string, indexPattern: IndexPattern): boolean; +export function isScriptedField( + fieldName: string | IndexPatternField, + indexPattern?: IndexPattern +) { + if (typeof fieldName === 'string') { + const field = indexPattern?.getFieldByName(fieldName); + return field && field.scripted; + } + return fieldName.scripted; +} + +export function getFieldsByValidationState( + newIndexPattern: IndexPattern, + column?: GenericIndexPatternColumn, + field?: string | IndexPatternField +): { + allFields: Array; + validFields: string[]; + invalidFields: string[]; +} { + const newFieldNames: string[] = []; + if (column && 'sourceField' in column) { + if (column.sourceField) { + newFieldNames.push(column.sourceField); + } + if (isColumnOfType('terms', column)) { + newFieldNames.push(...(column.params?.secondaryFields ?? [])); + } + } + if (field) { + newFieldNames.push(typeof field === 'string' ? field : field.name || field.displayName); + } + const newFields = newFieldNames.map((fieldName) => newIndexPattern.getFieldByName(fieldName)); + // lodash groupby does not provide the index arg, so had to write it manually :( + const validFields: string[] = []; + const invalidFields: string[] = []; + // mind to check whether a column was passed, in such case single term with scripted field is ok + const canAcceptScripted = Boolean(column && newFields.length === 1); + newFieldNames.forEach((fieldName, i) => { + const newField = newFields[i]; + const isValid = + newField && + supportedTypes.has(newField.type) && + newField.aggregatable && + (!newField.aggregationRestrictions || newField.aggregationRestrictions.terms) && + (canAcceptScripted || !isScriptedField(newField)); + + const arrayToPush = isValid ? validFields : invalidFields; + arrayToPush.push(fieldName); + }); + + return { + allFields: newFields, + validFields, + invalidFields, + }; +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index e30b3bbe8c0b5..68df9ff444fc5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -26,19 +26,26 @@ import type { DataType } from '../../../../types'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { ValuesInput } from './values_input'; -import { getInvalidFieldMessage, isColumnOfType } from '../helpers'; -import { FieldInputs, MAX_MULTI_FIELDS_SIZE } from './field_inputs'; +import { getInvalidFieldMessage } from '../helpers'; +import { FieldInputs, getInputFieldErrorMessage, MAX_MULTI_FIELDS_SIZE } from './field_inputs'; import { FieldInput as FieldInputBase, getErrorMessage, } from '../../../dimension_panel/field_input'; import type { TermsIndexPatternColumn } from './types'; -import type { IndexPattern, IndexPatternField } from '../../../types'; +import type { IndexPatternField } from '../../../types'; import { getDisallowedTermsMessage, getMultiTermsScriptedFieldErrorMessage, + getFieldsByValidationState, isSortableByColumn, } from './helpers'; +import { + DEFAULT_MAX_DOC_COUNT, + DEFAULT_SIZE, + MAXIMUM_MAX_DOC_COUNT, + supportedTypes, +} from './constants'; export function supportsRarityRanking(field?: IndexPatternField) { // these es field types can't be sorted by rarity @@ -79,27 +86,12 @@ function ofName(name?: string, count: number = 0, rare: boolean = false) { }); } -function isScriptedField(field: IndexPatternField): boolean; -function isScriptedField(fieldName: string, indexPattern: IndexPattern): boolean; -function isScriptedField(fieldName: string | IndexPatternField, indexPattern?: IndexPattern) { - if (typeof fieldName === 'string') { - const field = indexPattern?.getFieldByName(fieldName); - return field && field.scripted; - } - return fieldName.scripted; -} - // It is not always possible to know if there's a numeric field, so just ignore it for now function getParentFormatter(params: Partial) { return { id: params.secondaryFields?.length ? 'multi_terms' : 'terms' }; } const idPrefix = htmlIdGenerator()(); -const DEFAULT_SIZE = 3; -// Elasticsearch limit -const MAXIMUM_MAX_DOC_COUNT = 100; -export const DEFAULT_MAX_DOC_COUNT = 1; -const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']); export const termsOperation: OperationDefinition = { type: 'terms', @@ -112,30 +104,18 @@ export const termsOperation: OperationDefinition { - const secondaryFields = new Set(); - if (targetColumn.params?.secondaryFields?.length) { - targetColumn.params.secondaryFields.forEach((fieldName) => { - if (!isScriptedField(fieldName, indexPattern)) { - secondaryFields.add(fieldName); - } - }); - } - if (sourceColumn && 'sourceField' in sourceColumn && sourceColumn?.sourceField) { - if (!isScriptedField(sourceColumn.sourceField, indexPattern)) { - secondaryFields.add(sourceColumn.sourceField); - } - } - if (sourceColumn && isColumnOfType('terms', sourceColumn)) { - if (sourceColumn?.params?.secondaryFields?.length) { - sourceColumn.params.secondaryFields.forEach((fieldName) => { - if (!isScriptedField(fieldName, indexPattern)) { - secondaryFields.add(fieldName); - } - }); - } - } - if (field && !isScriptedField(field)) { - secondaryFields.add(field.name); + const secondaryFields = new Set( + getFieldsByValidationState(indexPattern, targetColumn).validFields + ); + + const validFieldsToAdd = getFieldsByValidationState( + indexPattern, + sourceColumn, + field + ).validFields; + + for (const validField of validFieldsToAdd) { + secondaryFields.add(validField); } // remove the sourceField secondaryFields.delete(targetColumn.sourceField); @@ -155,27 +135,12 @@ export const termsOperation: OperationDefinition('terms', sourceColumn)) { - counter += - sourceColumn.params.secondaryFields?.filter((f) => { - return !isScriptedField(f, indexPattern) && !originalTerms.has(f); - }).length ?? 0; - } - } - } + const { validFields } = getFieldsByValidationState(indexPattern, sourceColumn, field); + const counter = validFields.filter((fieldName) => !originalTerms.has(fieldName)).length; // reject when there are no new fields to add if (!counter) { return false; @@ -209,14 +174,15 @@ export const termsOperation: OperationDefinition { + return getFieldsByValidationState(newIndexPattern, column).invalidFields; + }, isTransferable: (column, newIndexPattern) => { - const newField = newIndexPattern.getFieldByName(column.sourceField); + const { allFields, invalidFields } = getFieldsByValidationState(newIndexPattern, column); return Boolean( - newField && - supportedTypes.has(newField.type) && - newField.aggregatable && - (!newField.aggregationRestrictions || newField.aggregationRestrictions.terms) && + allFields.length && + invalidFields.length === 0 && (!column.params.otherBucket || !newIndexPattern.hasRestrictions) ); }, @@ -446,6 +412,7 @@ export const termsOperation: OperationDefinition ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index 7a42560751273..b2d202763afe1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -59,7 +59,8 @@ const defaultProps = { data: dataPluginMock.createStartContract(), http: {} as HttpSetup, indexPattern: createMockedIndexPattern(), - operationDefinitionMap: {}, + // need to provide the terms operation as some helpers use operation specific features + operationDefinitionMap: { terms: termsOperation as unknown as GenericOperationDefinition }, isFullscreen: false, toggleFullscreen: jest.fn(), setIsCloseable: jest.fn(), @@ -1016,7 +1017,7 @@ describe('terms', () => { ).toBeTruthy(); }); - it('should show an error message when field is invalid', () => { + it('should show an error message when first field is invalid', () => { const updateLayerSpy = jest.fn(); const existingFields = getExistingFields(); const operationSupportMatrix = getDefaultOperationSupportMatrix('col1', existingFields); @@ -1049,7 +1050,7 @@ describe('terms', () => { ).toBe('Invalid field. Check your data view or pick another field.'); }); - it('should show an error message when field is not supported', () => { + it('should show an error message when first field is not supported', () => { const updateLayerSpy = jest.fn(); const existingFields = getExistingFields(); const operationSupportMatrix = getDefaultOperationSupportMatrix('col1', existingFields); @@ -1083,6 +1084,74 @@ describe('terms', () => { ).toBe('This field does not work with the selected function.'); }); + it('should show an error message when any field but the first is invalid', () => { + const updateLayerSpy = jest.fn(); + const existingFields = getExistingFields(); + const operationSupportMatrix = getDefaultOperationSupportMatrix('col1', existingFields); + + layer.columns.col1 = { + label: 'Top value of geo.src + 1 other', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + size: 3, + orderDirection: 'asc', + secondaryFields: ['unsupported'], + }, + sourceField: 'geo.src', + } as TermsIndexPatternColumn; + const instance = mount( + + ); + expect( + instance.find('[data-test-subj="indexPattern-field-selection-row"]').first().prop('error') + ).toBe('Invalid field: "unsupported". Check your data view or pick another field.'); + }); + + it('should show an error message when any field but the first is not supported', () => { + const updateLayerSpy = jest.fn(); + const existingFields = getExistingFields(); + const operationSupportMatrix = getDefaultOperationSupportMatrix('col1', existingFields); + + layer.columns.col1 = { + label: 'Top value of geo.src + 1 other', + dataType: 'date', + isBucketed: true, + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + size: 3, + orderDirection: 'asc', + secondaryFields: ['timestamp'], + }, + sourceField: 'geo.src', + } as TermsIndexPatternColumn; + const instance = mount( + + ); + expect( + instance.find('[data-test-subj="indexPattern-field-selection-row"]').first().prop('error') + ).toBe('Invalid field: "timestamp". Check your data view or pick another field.'); + }); + it('should render the an add button for single layer, but no other hints', () => { const updateLayerSpy = jest.fn(); const existingFields = getExistingFields(); @@ -1370,6 +1439,38 @@ describe('terms', () => { ); }); + it('should filter fields with unsupported types when in multi terms mode', () => { + const updateLayerSpy = jest.fn(); + const existingFields = getExistingFields(); + const operationSupportMatrix = getDefaultOperationSupportMatrix('col1', existingFields); + + (layer.columns.col1 as TermsIndexPatternColumn).params.secondaryFields = ['memory']; + const instance = mount( + + ); + + // get inner instance + expect( + instance.find('[data-test-subj="indexPattern-dimension-field-0"]').at(1).prop('options') + ).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + options: expect.arrayContaining([ + expect.not.objectContaining({ 'data-test-subj': 'lns-fieldOption-timestamp' }), + ]), + }), + ]) + ); + }); + it('should limit the number of multiple fields', () => { const updateLayerSpy = jest.fn(); const existingFields = getExistingFields(); @@ -2350,4 +2451,47 @@ describe('terms', () => { }); }); }); + + describe('getNonTransferableFields', () => { + it('should return empty array if all fields are transferable', () => { + expect( + termsOperation.getNonTransferableFields?.( + createMultiTermsColumn(['source']), + defaultProps.indexPattern + ) + ).toEqual([]); + expect( + termsOperation.getNonTransferableFields?.( + createMultiTermsColumn(['source', 'bytes']), + defaultProps.indexPattern + ) + ).toEqual([]); + expect( + termsOperation.getNonTransferableFields?.( + createMultiTermsColumn([]), + defaultProps.indexPattern + ) + ).toEqual([]); + expect( + termsOperation.getNonTransferableFields?.( + createMultiTermsColumn(['source', 'geo.src']), + defaultProps.indexPattern + ) + ).toEqual([]); + }); + it('should return only non transferable fields (invalid or not existence)', () => { + expect( + termsOperation.getNonTransferableFields?.( + createMultiTermsColumn(['source', 'timestamp']), + defaultProps.indexPattern + ) + ).toEqual(['timestamp']); + expect( + termsOperation.getNonTransferableFields?.( + createMultiTermsColumn(['source', 'unsupported']), + defaultProps.indexPattern + ) + ).toEqual(['unsupported']); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx index 161d8b63bd12d..b5f82653565c8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/utils.tsx @@ -33,7 +33,8 @@ import { FiltersIndexPatternColumn, isQueryValid } from './operations/definition import { checkColumnForPrecisionError, Query } from '../../../../../src/plugins/data/common'; import { hasField } from './pure_utils'; import { mergeLayer } from './state_helpers'; -import { DEFAULT_MAX_DOC_COUNT, supportsRarityRanking } from './operations/definitions/terms'; +import { supportsRarityRanking } from './operations/definitions/terms'; +import { DEFAULT_MAX_DOC_COUNT } from './operations/definitions/terms/constants'; import { getOriginalId } from '../../common/expressions'; export function isColumnInvalid( diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 412b3706f278c..6ab97b08c93d1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -468,7 +468,6 @@ "xpack.lens.indexPattern.fieldItem.visualizeGeoFieldLinkText": "Mapsで可視化", "xpack.lens.indexPattern.fieldItemTooltip": "可視化するには、ドラッグアンドドロップします。", "xpack.lens.indexPattern.fieldNoOperation": "フィールド{field}は演算なしで使用できません", - "xpack.lens.indexPattern.fieldNotFound": "フィールド {invalidField} が見つかりませんでした", "xpack.lens.indexPattern.fieldPanelEmptyStringValue": "空の文字列", "xpack.lens.indexPattern.fieldPlaceholder": "フィールド", "xpack.lens.indexPattern.fieldStatsButtonAriaLabel": "プレビュー{fieldName}:{fieldType}", @@ -480,7 +479,6 @@ "xpack.lens.indexPattern.fieldStatsNoData": "このフィールドは空です。500件のサンプリングされたドキュメントに存在しません。このフィールドを構成に追加すると、空白のグラフが作成される場合があります。", "xpack.lens.indexPattern.fieldTimeDistributionLabel": "時間分布", "xpack.lens.indexPattern.fieldTopValuesLabel": "トップの値", - "xpack.lens.indexPattern.fieldWrongType": "フィールド{invalidField}の型が正しくありません", "xpack.lens.indexPattern.filterBy.clickToEdit": "クリックして編集", "xpack.lens.indexPattern.filterBy.emptyFilterQuery": "(空)", "xpack.lens.indexPattern.filterBy.label": "フィルタリング条件", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0b97869ad0a4a..93878dc3ba43b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -473,7 +473,6 @@ "xpack.lens.indexPattern.fieldItem.visualizeGeoFieldLinkText": "在 Maps 中可视化", "xpack.lens.indexPattern.fieldItemTooltip": "拖放以可视化。", "xpack.lens.indexPattern.fieldNoOperation": "没有运算,无法使用字段 {field}", - "xpack.lens.indexPattern.fieldNotFound": "未找到字段 {invalidField}", "xpack.lens.indexPattern.fieldPanelEmptyStringValue": "空字符串", "xpack.lens.indexPattern.fieldPlaceholder": "字段", "xpack.lens.indexPattern.fieldStatsButtonAriaLabel": "预览 {fieldName}:{fieldType}", @@ -485,7 +484,6 @@ "xpack.lens.indexPattern.fieldStatsNoData": "此字段为空,因为它不存在于 500 个采样文档中。将此字段添加到配置可能会导致空图表。", "xpack.lens.indexPattern.fieldTimeDistributionLabel": "时间分布", "xpack.lens.indexPattern.fieldTopValuesLabel": "排名最前值", - "xpack.lens.indexPattern.fieldWrongType": "字段 {invalidField} 的类型不正确", "xpack.lens.indexPattern.filterBy.clickToEdit": "单击以编辑", "xpack.lens.indexPattern.filterBy.emptyFilterQuery": "(空)", "xpack.lens.indexPattern.filterBy.label": "筛选依据", From 2836018b0ea414c4cabefced0f9eacf73b9e9244 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 9 Mar 2022 12:49:10 +0200 Subject: [PATCH 101/140] [Gauge] Custom color support (#126882) * Fixed case if uiState is undefined. * Fixed wrong behavior of the coloring in the percentage mode * Update src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx Co-authored-by: Stratoula Kalafateli * Update src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx Co-authored-by: Stratoula Kalafateli * Fixed bug, related to the formatting behavior while picking overrided color. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli --- .../common/types/expression_renderers.ts | 2 + .../components/gauge_component.test.tsx | 11 ++++ .../public/components/gauge_component.tsx | 62 ++++++++++++++++--- .../expression_renderers/gauge_renderer.tsx | 2 + 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts index 9e72a0eabef65..4c2133e8572f8 100644 --- a/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { PersistedState } from '../../../../visualizations/public'; import type { ChartsPluginSetup, PaletteRegistry } from '../../../../charts/public'; import type { IFieldFormat, SerializedFieldFormat } from '../../../../field_formats/common'; import type { GaugeExpressionProps } from './expression_functions'; @@ -16,6 +17,7 @@ export type GaugeRenderProps = GaugeExpressionProps & { formatFactory: FormatFactory; chartsThemeService: ChartsPluginSetup['theme']; paletteService: PaletteRegistry; + uiState: PersistedState; }; export interface ColorStop { diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx index c71b95a95d02c..e7e1e47ca65f2 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx @@ -79,6 +79,16 @@ const createData = ( }; }; +const mockState = new Map(); +const uiState = { + get: jest + .fn() + .mockImplementation((key, fallback) => (mockState.has(key) ? mockState.get(key) : fallback)), + set: jest.fn().mockImplementation((key, value) => mockState.set(key, value)), + emit: jest.fn(), + setSilent: jest.fn(), +} as any; + describe('GaugeComponent', function () { let wrapperProps: GaugeRenderProps; @@ -89,6 +99,7 @@ describe('GaugeComponent', function () { args, formatFactory: formatService.deserialize, paletteService: await paletteThemeService.getPalettes(), + uiState, }; }); diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx index dfb560a33b092..9db6b81acefce 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx @@ -8,6 +8,7 @@ import React, { FC, memo, useCallback } from 'react'; import { Chart, Goal, Settings } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n-react'; +import { FieldFormat } from '../../../../field_formats/common'; import type { CustomPaletteState, PaletteOutput } from '../../../../charts/public'; import { EmptyPlaceholder } from '../../../../charts/public'; import { isVisDimension } from '../../../../visualizations/common/utils'; @@ -183,8 +184,23 @@ const calculateRealRangeValueMax = ( return max; }; +const getPreviousSectionValue = (value: number, bands: number[]) => { + // bands value is equal to the stop. The purpose of this value is coloring the previous section, which is smaller, then the band. + // So, the smaller value should be taken. For the first element -1, for the next - middle value of the previous section. + + let prevSectionValue = value - 1; + const valueIndex = bands.indexOf(value); + const prevBand = bands[valueIndex - 1]; + const curBand = bands[valueIndex]; + if (valueIndex > 0) { + prevSectionValue = value - (curBand - prevBand) / 2; + } + + return prevSectionValue; +}; + export const GaugeComponent: FC = memo( - ({ data, args, formatFactory, paletteService, chartsThemeService }) => { + ({ data, args, uiState, formatFactory, paletteService, chartsThemeService }) => { const { shape: gaugeType, palette, @@ -230,6 +246,34 @@ export const GaugeComponent: FC = memo( [paletteService] ); + // Legacy chart was not formatting numbers, when was forming overrideColors. + // To support the behavior of the color overriding, it is required to skip all the formatting, except percent. + const overrideColor = useCallback( + (value: number, bands: number[], formatter?: FieldFormat) => { + const overrideColors = uiState?.get('vis.colors') ?? {}; + const valueIndex = bands.findIndex((band, index, allBands) => { + if (index === allBands.length - 1) { + return false; + } + + return value >= band && value < allBands[index + 1]; + }); + + if (valueIndex < 0 || valueIndex === bands.length - 1) { + return undefined; + } + const curValue = bands[valueIndex]; + const nextValue = bands[valueIndex + 1]; + + return overrideColors[ + `${formatter?.convert(curValue) ?? curValue} - ${ + formatter?.convert(nextValue) ?? nextValue + }` + ]; + }, + [uiState] + ); + const table = data; const accessors = getAccessorsFromArgs(args, table.columns); @@ -353,12 +397,16 @@ export const GaugeComponent: FC = memo( bandFillColor={ colorMode === GaugeColorModes.PALETTE ? (val) => { - // bands value is equal to the stop. The purpose of this value is coloring the previous section, which is smaller, then the band. - // So, the smaller value should be taken. For the first element -1, for the next - middle value of the previous section. - let value = val.value - 1; - const valueIndex = bands.indexOf(val.value); - if (valueIndex > 0) { - value = val.value - (bands[valueIndex] - bands[valueIndex - 1]) / 2; + const value = getPreviousSectionValue(val.value, bands); + + const overridedColor = overrideColor( + value, + args.percentageMode ? bands : args.palette?.params?.stops ?? [], + args.percentageMode ? tickFormatter : undefined + ); + + if (overridedColor) { + return overridedColor; } return args.palette diff --git a/src/plugins/chart_expressions/expression_gauge/public/expression_renderers/gauge_renderer.tsx b/src/plugins/chart_expressions/expression_gauge/public/expression_renderers/gauge_renderer.tsx index ebd2aaa6fc948..c75f60f75c004 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/expression_renderers/gauge_renderer.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/expression_renderers/gauge_renderer.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; +import { PersistedState } from '../../../../visualizations/public'; import { ThemeServiceStart } from '../../../../../core/public'; import { KibanaThemeProvider } from '../../../../kibana_react/public'; import { ExpressionRenderDefinition } from '../../../../expressions/common/expression_renderers'; @@ -40,6 +41,7 @@ export const gaugeRenderer: ( formatFactory={getFormatService().deserialize} chartsThemeService={getThemeService()} paletteService={getPaletteService()} + uiState={handlers.uiState as PersistedState} />

6ZKVPqEpGA5MIm#Uzy+9}t_d|&nR z3E$0pNEF|Q&z_`FB%c`0^@P>R2_R^;{KzWdHgP-(j{>lx>m71%XzNc=V-w*Y3aNQo zW|&bhioK4EI}?<<&?hR&H9?-7CJ8Q5uc5?Zv$+RVk=g)0PZ0$H4dUL zvS?QE){Vk=Avil7ym#zBM-fJ8MfTSX(tS7b+#g2h=2ofQ2}%}a=+2H+MP8!G#PUYM zRFWqI8OXFQB)zFFkg+GEU;<#jsl)ov*S{{6?KfBzA+e+xu%z{vdNIl#xPlRbhQ5=* z8p;9vdC*LavrmgfPwvM|$u?qt|KmkI-1vir7NoJ~`;EecV%Y3io-3OY;MS z5at=Gj#_l&+D!ggWtfVsrul?lPqRxE;+aF=_;bXHbR4EehBj+0P<_#2zTl6ZgtQ*C z;7iso1`Hm+cpA5Uj#S0a0a`}4A^uiP9CI_c8;LJ-pQ(!c9V+8o7LQV=Q8YtT_t4Fn zFFTNHsIeOTIMYR=JhvR82%mN97-!2m@`5&&fS(7QWO{&*%eu4SEYs)^ieL&a!EZWr1v8NHlGD<2-0iJ0%nY~ zmwHg~5(fpPP7#9t$J9GGWcI#MzuC5J+ty^;wr$(CZM(^?$xYUT$xSsArkXmt@9%xj zIsd`a(|+!KU2A>Tx}{66S8f88`R{He0nTTfm(V23O*dz&5w-LAh12=6T9hFc_{z*) z6#OPBvWE=a+qX@p-UHe+v{NHc-Ii7~q1Lf~8&aHT(RQiV6YBPx`;eX~v+)+kdj8L5 z1W!SubO%Wm|1KwlE=lku>haSWI&WAAo&HHH@MG#Vbo#gL&-L!(W|!%Ou8L=HhB2f5 zZ(AXoxaogu9cS?l~|ZIf0{9E?iVeO5M)g;4t*7cYMZ3fG&@sd(x*p{ zb?tvVvJzu~b9NVQu4;!#nf)?W?X*$@>CPNkjT~M@&%WAZF7lV)P?cN2NXtdIqadKQ zZ$SP+l^0jxEIW<3S!g2=n}fG=E;n~oQRr;G8~C|Om?lVGB1Wy$#JvftS%tTM(=F0x zh}B4CeyeEUWcMD4W^)MW&wIcmMhZsOD;D7Zx0XUIk&M;$!T^S}sA$mR4wIi_w9VR1 z5yLLUZcXtVZ2{qti1AFK9;9t1f~L9xyyGiG-ITriUJkO^PVlg{GdH< zWm_CjR(WKMSwF~5IhjLTbv zzz`m2gO0@zJZIbimApG?7L82`^oFnBAd!3xms-sHgIXxcGHM@b%6Lu=Ud?}A<{v`= zx9-X!4j(A!Z5=4MXAbFP)ml*O3}{QgaoErWyF-Hh8nn(c^}@rA0)b`T5o_~}uy5>_ zW+xeuG|wn_5#Zhxl$bUIQi^=*Q#cTVNz8PTBNCpYQ@@m3|2g?dsS>Gi$}6We|7QRp zk(tKvkl!%QYm-JGk+?YDPtLR4EtoTMrJ_>b5Wx#!jf-6^A)Uk`LpUh3j8D+M4UKQx z+I-3S{q zM=#h`VbudLg%+-T5EgZSIUldWku=l`D%jlqef~%7l3Tb}^G}nF#bC@BCm{t8gh_8J z`_o3EU7M1f$P{h$Y+*!U$f!4{8Fyo;SC_a+tMn1k!~H#8KezkjByAC)W$feaf=5CY zV*K_Hi)f^b!zIZbxggnp5P=7KWj`@kukE;clI&fNpm+Pvd&B?%)AcTiND|Ex1cTO) z4g1N+!HKqW@qPTP7?JMYy^(w5wu&8@e1l}Qa$#47{gNL2*K{g#?5u|C(z|}<_dl-A z*mmsq%w+r7D!mjI5dV$@TM7S>AUo6eO6V-T5m_irQ^0 zfLJM$W43hv?aWZHAC(S@e=X1-xAST5Fh#0ta) z7aR4Yo&Bg3uy7{QZw-+yG#SAG6Nj9)P6vmS8isbR!O4|LGi#VnX*|;4svFyBtnt%B zo8Bhg1a5DV$5y&|Qx|bM?7%T43$*o{jmgz6K!8M@Sp*>FeRoRFsN329M5EgH&po(3 z;5Vi@1VCj}pn9h7alU@C%nnQ~l6i6hq}n<1M4Fk?bnLN%fQzs&O*Z?x9R7rim1G7u z2x|tbGYEIpR|YQBpGwqr)tfd#f>Fn519X@u|+5*AC8Pa}b^ zGm7DjOd-lh<-e@eMv9>M1ZOMo5Y&7{GpSssyQtR?L~XM7nv221FhL0YKC|_5G2X6U z)io^k4?xQ7MPO7%Av)?VAJ>65N?9t_ZOQ`_F{S$%2;pFYn)w5p2)>XQzGdg4jpry-SZ5} ztUD>CvTd8MBa<}iRr-Al!j66Ly%u(+MU#3<6^oKZ7K2NVp>RY+Pt;?Za8olT@~I&d z-AR$Urh2MPta+(m=e4IHLC2ewN6N^J!A!>U9vzVv5^vG|8lavVz>UJ`aaWb|yZj~0 zU7P;I{3GLhBBKzMbYhid`0>2Ap<}R;c4llxJPHZL4JU9SO!cPSw(R{4XN=%$T9d)W+Z9&N&IwQ;fmwf*@MTrZS=D&!5is(`0?Q`HPLYV1zEw|YS+fgR1K zI1)dK>>>+2qvw4;_T&a^w~l2C!GBblV{UVJO5ESh=8v*o@91V_pv0MNR~7L8Xfk38qH{gsc7F6$eUpy#!VaFDsC z;&SQ-+%IC}s1l9IO>J@1d%W>o;=fs6CvUs7${KClX_}5+*S1YV836J-(pLS;Ts_`GDPOPThkn9$I zy*fxKc=;O_N%<85!C*Ny*fvgZ84x&D*eP8bpCRM{Bggb!fiMgtjcaML_Xf{AV-VQY7>y6_F4*R_0~a6jMfc{9xO@**f~?X}vrfT3>(-@WFWOYhW-GWo3cD1sr%34mx8)ZrSXlsi%Ub)kGul>2KT zC|_YlflO&Ht^7qm2qg5lg(n|(BzH7NH{6Hj&V^?k$Xx!=2T+mR5=#^PqSDGpk)(E4 z{MvMTj}(Zk!(ZQp7Rgz%@br_cZ4v$`{{odh&q)TK_qFM8#0Sy%TKqP^3yoI+9?NqeETepL-D&Y+VUrNewT4Z|-#nW%q4;8rmy1yCuzI8m7c zLcsgfy?|Sjg#?xP#&aO^lr#Cd`ycVSzq!ee8Oe_JRqqRubdO_CS8O#n;MXps;p||D zrtJA3t2wOt02_#!D1!WNl~Khc46|}uK7q(%XdN2k_Z8oBu0!Zx_-Va{k9G@OAjM^c z_*jkYI+(Gt3)&gLp#qf4jz0Iw`2F&JZgRNTDUI6cx)M&v^JF8VMVxR0DS zK_hNTYtwXh0vLiVHpx0^^u_{ABp|W)ipQMD5g3Tl3s0Z*?fV(HL8pNw8pv_=XbW|^ zzlxNx`s@lsVT|Pj`36~vr-6O?8NOb4T&AaW++b$v(=U=Zz$AJHcaGyJPr{!k}};(|>=O7$!Cgh<8cu9S7BK#k`0U7c-c_&gkf9 z{QLK@>6j08t)_1oHQEetDA$};8SN@< zK?|zCT*3TK9Q#PAYjz#wKcokgQd{L4mmls?Uc#sulZhiA-1$-zzBxbDF=sk0EPv;F z?e+1?haRLFfBeP8I`(4vNAZWaH`F06MCBtk%+ZaE?cY@1a@M5|MYXuC@i1sWqQQ*3 zrXM0beyE6p>im^05~h6n5~99Y!I@z)|NTH8C@j_fLqJ`h&l-x7q}b;R;$;tsU53IB zrwwm+85TaR;9Ha;q|_#dJ)FN|myd zy2#5>`Ab2W?NhO5v;TK_kX?5razdds3+j>3<^(H&Sy3V$EXagYKGpN|uP5+%DX$Y5 zV7ZoW1bn8bBh^W!3B>~Q{D3kLT(&a5V( z6?WE~pCPp3TC!uz7l3cstC%5M?!RP zv_C5U96mjIDjM_g*VWgr3C!-p8*hLwgAGlu8Ri```=%+rziE3?3Ny+>RKWHC!epx< z@?(p!p}ziM0l5$9UE}_rCKU09CvC|P127u)Ra|lbFm~kzEV+v~^b}7(<%E}oXO08c z1l<^b+X^rL{*5$*ntQ(r@EPi%-UE7TOUSI(BvW_$=Sc?(rC;%2xX^AXZrq4<+lS+y zga_Ot%fK1Qjjdqj##kXboKwUe7e_DQu)hOR^%#WDwW3EYK^P!D{9%aX85^tN)X?1= zFs-+>>zUTPo}FMx2m$t)oR`WDGcg^DQ;m3EvP2sVwqfxrDhc2(@Q`=+irK_eQkS{- z?)gJmP?SYu6aup5^6^tG$bg2Qen>~J#)qm)p8HvGF-fiDaj;=PVMQ<7N=A~{3@$D%_^NjR z#z-YYdkm;-ZV%^>xj$dPbwhE_wc4o`H;$}(hkz4Co z$nX`UcaS{((*Wr}1r^sS0A|M}IKrdDNO7Lmg}gL{ML4*H#c3X#E}tofH}>8)#|~4V@>F z4z-3lN3f~E*7-)h*Hf-CAW;EUcGpq2OvN8xVUWRE<*8xg(7)`|(u=U>L)Uptwt>NY zc+4~q=n*FhMu%=^ilem;nDUBvZp9i%?bS9Cw~)`)az1h}VunV6aeqz{miJz$8dUYY z*0$}@^Wn91^y|QO?3Z#FF-U<@I)89BO#w0^%p{LKhad$t1Z7Q_GVmGmu;b>UJJv#i z!iU!a?}aU#_H<;(U#a&kXoiUfv^U}Z8uMS%(rNLM03gHH(J}R9j zd{bC?cW-@}&G|lc7FC58+4&82UbC{mV)8mgcc>|ZL)>LopD!RO^CL{3A~zXUl6;Xu ztQ`P-+^%eC6e#5M6!;fETi2^{mrBw7pu1koHqXx*Z`#x<&dAIS-d%Zp0(Fm~ zirr6YVczd#NPR*zl@|Bs`tj$$Gne&#A*Lj7vq&mlD+ZGP05FYmj8k|-5b+&Mo11&z z7vGICpGSxYK`GY|p-ugGk@9aY5T%5GQr`sJ5pA6r4MIjh!+fX;I9KW$mEbE|LO>Uk zA4AUga>D{l)bpkeaiA>+h7@-{R7?T>%c^%Yp_SS9Ued<$pUhj){>Ou8=dk@rr*E~5 zJM{xqv>PXnS~gA(j?5K4y)1SFg0)6WaReL!1T?Z>;)cSwMWyJ|LNN$AV}sX9T>0C@ z4um)k6o5>{vcwLG7#%WuY=2IrEU4N;5{uaK>ej{JvTs~qY`<>uck}*O=G3#9;eY!V zHr%)Ex>SCTP);V!{L*E6?y~x zQqwo7`U0It!n9a*+1bQ!2Sx4>Tpge}CwFmifs+!29i*RVHnDv-m@~(e05hHg;@i1< zCRCT&$u7lq9)~lEl{iqG3N-q)KPrS5b_9Kx1*{aZr1yGPuytTs)}Ixrn+@JCtlhwx zA$(^Meb(;k0G0w;C^J+GiGE4kUnSzuFw=&XhcBn&BM3C##Q}6#+pAicqoN_t&`Bz- zD&{%X`UgdWO?-+SUvGLEQaEMX7nv6%+{+^2&MECMT}3AZar?JFVe3u}~ zJW&I#_a?qTlB>cM){A7Hc#OJ%>Z1unbaGl6T_W?4y#hCqDI*oQa0WQo;xBp#dqTEQ ze@vzJSbYI+RD6u>pR8HQxXlLkAn()4l>)!(o zzK3|F^ee~SDuD7nZGV^jrfZby=wrm~Qfza2yQ<+`lj-H|BK*K|(E!|WYYTOSI?5&!_`JjBv`ms#!Ufq8XyS2;XKG%{*qAM zGKO@j@*ds4^QPC?ldfO4h%jiB9>^ zFcbF}=~CJ2%gttF=oy$1cwl8;{WQBFY!%PUUbp}BqL;IEQ1lM=q8TAH&p^;7m5tvg zh$HPKom~8xXg|I#>2VC~QTI!hQUz3Wo#pJ6@-iK4^uSC5R){=8K^#IzcNFjtuURL*Q_v|uZ5uj6G2?j*sS$O6H9DgCd6v7$5Gg$W;BabLFZ-XaK4nG z_hY(GG;c9lEvZ<<8IMe%HrDFO13nM*6$}@6AN4|f7WC7HVn4y1aOy%22WTJAj%AkP z|BJ+ND4`$>vFT}*ue!T_woH!mn!eV}xXOcpFg=vdKXNKw%2yrx3v~2eOcsnfwm73; zgJd2CpK~4tgn}l{R|U9mm4IRhQt)bY%T8gjU1P!oQGiy}wz^6eo8qpo%fHt-ye&FQ zcWA^@z=Njh!ienbwc9DrYqf6b3-KlZo=+y5FVcE#1)tyCWi@1DS-_9p3;SOnSE(Uv z!wY}vp|I5csn)9%$<_h`=%`?)nhN81eiK@4fONI(VMrDCY&%9L1GauGWqllf{5wI2qsZKgbLF=Ou z!tpE+@GNgJ=)X><0-W>jj22Iri7l)YNn@#Kgr#h+BnE)VLJdKEqO)1VBb~uPI(x!# z9G8we`)#%^yC$-^r6|d=z8BbWJF?o}R!m&)5m%bT3 z{=l6LFQZD^$P%|*kX@_yKo{*&9tlg8a>=y;jLH^>Wbis+)y#|DX&$2Sqv=qIEl0-& zIs#kBQI0$|zJ1h*c?OfyJ^2qEKgv%d(|$f$WHplug(=5!3aS0ugU$$c-ojM}EPfLN zSe&9a-e@2Q<^REk#OqR8pH}I~+Z9yX-kM*U96k>GvK!^q6JtThj}E1XhQ2VGB`1ze zjSOXuO>s-oG6`Q23b*RHK$@w3H$+H_`?7Xj1ALEMh1z4}thusBGWRwdzR-bSsk02+ zQWWFt!>^_t7h;gSdu;81#(Y5S%}xTguDeIZy0+(9D0g+mpd^iP$=yV9-m<*=CW*R5WEz6 ziy}sRefzJk%0h$D?n;PDnMhVmPsXsP?ujk91ux$$R%$@}Ebdpcqtd7ORcT$~G48w4 zn@a5uy^-~n_M)noCF66{z*up9jg zK!_#l+j47srwLTUzpN*Lux-L@Od1V;5_w;?}u zJZX3eaQ#^to;Zu(HJWH!3Sov|e$SFiE}f6=AkL<2lISB*wwdb!`W{dZ6Q<#9JH?AEoq zkl+%4h!(GbT|D*g4ea*1O)205hIz8GihmwjxLXPZw6}7;DZAMu`MxwfpV@|KH!BQi z1s$C^guBW_`= zha8CzoDwm37gBar?q@Q4Ipb27%*{xcl~)-RKSJSdf-?F)XpUnq;2vS>mge#M7TavS z@(APL)q_L?Gj@(YyP~B#=%QlnCL-_!Xb@Dt@*h!cH&CECU^1lHpD>`yX{Z1X9~^I7 zHkrF6HMijNWy)*rI86WV4{4IK$}pr?yZ8IKWEXzWTS{ZR>NId?o`Gs1e(fuQ04LQv zkTH2<`K}-P*~P;PJ)AsL=i_3)7Mk6-9b)yA+ zw`ei=6@gQrmzsevdC#`x zF%K~(SFX4WYAqpHqOFq#ZXNQCt&h2s(P)6=)@^xOAh2ee=?(^H3?B`EP8PMJb1TGTFg@Dj7o}Bsqb%6cS+6?2-b* zfA~Zn_v`sxHb~UOU4LT)vYhBLU0EB<@xj#kYw2UkB(%8Zrl%AZpxpf|9FVWtP zNrKi#w`e>OVTNXTp0OLHsmM$ppG*SHg|nMzI|Wrq>iw?VU2d%1ank^NAp*_?WWoQP zb_qjUVoC-^X-oGq7f4=^K2F$b>D@^~{-W+Gri@CiH3`-A_VBX@Rsm5tV?gHF&PDq@z0Q4>J(|91bo-91aIa;m&?nJ0wUJqIX{VFLb_rPAMK4pG#( zg@f!-%ON^MWr6V-yA=#$GohOt4{g5hfKlNQ%I*B5Z_>#n{a1^&3Hjg7O=6sLpg9keu>$E^|nL%c|W zIxD^|7CAM1zALS)!)M(py8dg4+HleX0H8*8`wyVD$cvL?q&RQDQWXhBqH)O2=&8 zLD?hZ1YV1zPTRVlluXmoxuDd&~CLCpp?7agV)!Cz-w*e6u{u}CA>e*!)o0WRVS6+a_mtHKiu3J z$>Z!Cj|tSEYy5M*BTIpdln{)>qz5@cB1_9+=yCnOu?o99kmJ3T;U?1_eDTdPBnMM_ zj2pY$A@n_eZNpSz#=1=-@|1olvOX>=x#nq0m~SMDVG3}_AY9{eVzp(fv&B*WD-zKW%{Z!s>Ez78bLKSupfe8^6y*<%GhSV0P>GfP*WCR&b5IcG!myH8rZ{!1k} zAkafUS9Va7RcEXcX74#zSHYLWCP_8RiFKl}41kBVT!>UX;atJ&nux#L4TlX25U5m9^Y9r2b( zgPnuBc#3u8A+Cuph77Sb_^wJh5EXJINHJVp)?GRu26V8JFBy^XJP&x%dY3=U0a}G9 zC0J{YMX}e00Jt*Q<$>U<&2Qr|2YM`0Id%FU=$dHM2K}K~im!!gn(C!L%GIhODa>Pg-1hjboeCzzw*0{KEj2*PtO zO)v}5>f&-UW!AF?Ga5;Jgu>TyEe+pwAqiaUsx>{Xlo(5h!wNXj_c!W|u()fzKb^gD z3;WyN(@J~RB~n)UrMCJqx>L&C5U|Fjv}^v2LpcL)5iYfLhZbPF`&r7UY`%jZn;u;P z9xZPuYJO{HO0shvO^Uzw3tfyUE#%De9TcXu$73P~iJ&%6k*&-es^fZ1fXe0cpbU8B zO@N%+;;NX7b`ub0cA;|On%oZHN{bl=36)JHPheSgmO*6ExON!uw0|>}_N2d8hMhf}_coA>gM9AzAq!mv;q1-Z5pN z#)dlde0^fs`Z0BHLC2+umhUKr?z-d0^Q^5Yyh|u4miEW}H%4LbandVhBliy=4E~R# z_+G+ziUVc8^ec~q+vD^rR*VC|J)ZM4vDb7wDE~d~3H#8)MvBT7AwRGNkdDb~xkoY_ zCPhE3wz9xQL6fjaPbC44QDX#6vkX3;C% z+)drwTc#QVzGE2HG)@Nu(5FC+wk^)Y{!y9pymY%e^QT7Jbw9=Zc^jVvRO8eh4o!OT z%O&&w$;3!WlFhV^6t(iRtpOE~=FxzKe7)+S_etPfbFP*ay#VD;E+R+_h1 zchkO)`=V7hE z*AmL|Du=mYHnza5qqEg-E4#V2@ZWfC73>CMGOZucha56^17c8zjI~|;?N)1Nq zNS6H4+TX29jjeSf%SJE;3=V1@|0Q|}P2;OSvm%!DTcPJ&({)qY-2HDU$9e7guUCFH zg(FHNDqI6^)j$2O1MXH{@2hL>?vnhOVOPiF!j4?q9HSs)>}5H{BTKUZ6XGvKIhkf-=Fsl3f=&nNw7k*3HadpIbTJs}T`2H`D8Cr)h zV@-zVoAeba+gN=1@Dw!gru1x!vuyo9{(W z0z_6_<|!YWlOrX3_I~+g7qv`|L+d3pw>!1v)iPE>#T!m=)g7Ob&`}QZ8DwQ*W&vmQu2sLu8Ms~nYjs{OxC01epm!jG+T~$ z^xc9(PEK>FKh?7!vm^Gr=){*Te8f3=#HKE5@4d>GK&xj0s=ZiD!V}jSt?)B^Qm)xA zMdsKRDNZNMosm%}K-CkuePl;`T>2z*vR?`?6~EM9-VRy0ywV^We+p2y@P{bfpb|D# z*RXHfkGgNhE+E)GkAl~WV`4NRK((S`y}#NOT;-)Z6_&y?Md4Scn*eD%LG+f>_jU77 z>JXPP1c!OL`DZOxmkdYG_sxM(T(nv!ttyI%cfUh9k>*d&Zd&z%vNhQ^=4B)b0Gxv( znX}#QPTxHHxjF>_%GGFjyIpO(G{&sO226N^RiloN?gXG)CxfblLxtn+Ws`_M4ALJ< zLq6QLz@$Q1YPAsCK~q1C()c@J?3xX}{kGY1&R`05c`F<|kYn&^M`)1gL*Do?Y-KNL zJ!`wLD;SoPSfdxji}+jp1DT9v<0Re^94|>m@A+kP9h64@hq6xh=uYf>-bGTF7#*z zg7p_MLZDnDe~G*;NRGm$b z=rU&CED=|=D9&!Cig`m_(c63$I_cp zriIATs!V5{mg7!pr{ZR*VSszFa(*6SIU$pz*zZAjMtg3?8-0TBpG{uqz&#$Zg8>#G8_5WMW(nwu_h_I&GGIM|# zW`OSooAzTyDxr%JxE@jv&>%K2hpL}nuGHCg^dbgM?7iV)Ac3J*QNyblBiO&DN$sl1oAjYv-T5HqeRm?!52QNQh)w@7+ zfGpADa}7{iEzzVY1&@4tT;^}q%a96WN}HRovN2zQz?xf+d2Y34rE;iG_?pr-AVu;T zEppLE&I#+tegDNBip=be;tOhYFP0KuHHm+7<{0>Czh@qm(muS5!rsNDZQ>{hYp%)! z0b&@lVsKvzvA*q}W`N-B^w?kI?HuQR#a^I50dLwYh|wTNb4sZ(wE_B*3^@RJGv$y zVV3F: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 z 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 literal 0 HcmV?d00001 diff --git a/x-pack/test/performance/es_archives/ecommerce_sample_data/mappings.json b/x-pack/test/performance/es_archives/ecommerce_sample_data/mappings.json new file mode 100644 index 0000000000000..8b2386508f26b --- /dev/null +++ b/x-pack/test/performance/es_archives/ecommerce_sample_data/mappings.json @@ -0,0 +1,219 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "kibana_sample_data_ecommerce", + "mappings": { + "properties": { + "category": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "currency": { + "type": "keyword" + }, + "customer_birth_date": { + "type": "date" + }, + "customer_first_name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "customer_full_name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "customer_gender": { + "type": "keyword" + }, + "customer_id": { + "type": "keyword" + }, + "customer_last_name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "customer_phone": { + "type": "keyword" + }, + "day_of_week": { + "type": "keyword" + }, + "day_of_week_i": { + "type": "integer" + }, + "email": { + "type": "keyword" + }, + "event": { + "properties": { + "dataset": { + "type": "keyword" + } + } + }, + "geoip": { + "properties": { + "city_name": { + "type": "keyword" + }, + "continent_name": { + "type": "keyword" + }, + "country_iso_code": { + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "type": "keyword" + } + } + }, + "manufacturer": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "order_date": { + "type": "date" + }, + "order_id": { + "type": "keyword" + }, + "products": { + "properties": { + "_id": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "base_price": { + "type": "half_float" + }, + "base_unit_price": { + "type": "half_float" + }, + "category": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "created_on": { + "type": "date" + }, + "discount_amount": { + "type": "half_float" + }, + "discount_percentage": { + "type": "half_float" + }, + "manufacturer": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "min_price": { + "type": "half_float" + }, + "price": { + "type": "half_float" + }, + "product_id": { + "type": "long" + }, + "product_name": { + "analyzer": "english", + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "quantity": { + "type": "integer" + }, + "sku": { + "type": "keyword" + }, + "tax_amount": { + "type": "half_float" + }, + "taxful_price": { + "type": "half_float" + }, + "taxless_price": { + "type": "half_float" + }, + "unit_discount_amount": { + "type": "half_float" + } + } + }, + "sku": { + "type": "keyword" + }, + "taxful_total_price": { + "type": "half_float" + }, + "taxless_total_price": { + "type": "half_float" + }, + "total_quantity": { + "type": "integer" + }, + "total_unique_products": { + "type": "integer" + }, + "type": { + "type": "keyword" + }, + "user": { + "type": "keyword" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/performance/kbn_archives/promotion_tracking_dashboard.json b/x-pack/test/performance/kbn_archives/promotion_tracking_dashboard.json new file mode 100644 index 0000000000000..7db22ec7e6d0c --- /dev/null +++ b/x-pack/test/performance/kbn_archives/promotion_tracking_dashboard.json @@ -0,0 +1,7878 @@ +{ + "attributes": { + "fieldAttrs": "{\"products.manufacturer\":{\"count\":1},\"products.price\":{\"count\":1},\"products.product_name\":{\"count\":1},\"total_quantity\":{\"count\":1}}", + "fieldFormatMap": "{\"taxful_total_price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.[00]\"}},\"products.price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.00\"}},\"taxless_total_price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.00\"}},\"products.taxless_price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.00\"}},\"products.taxful_price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.00\"}},\"products.min_price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.00\"}},\"products.base_unit_price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.00\"}},\"products.base_price\":{\"id\":\"number\",\"params\":{\"pattern\":\"$0,0.00\"}}}", + "fields": "[]", + "runtimeFieldMap": "{}", + "timeFieldName": "order_date", + "title": "kibana_sample_data_ecommerce", + "typeMeta": "{}" + }, + "coreMigrationVersion": "8.1.0", + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYwMiwxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "5ed846c2-a70b-4d9c-a244-f254bef763b8": { + "columnOrder": [ + "d77cdd24-dedc-48dd-9a4b-d34c6f1a6c46", + "7ac31901-277a-46e2-8128-8d684b2c1127" + ], + "columns": { + "7ac31901-277a-46e2-8128-8d684b2c1127": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Items", + "operationType": "count", + "scale": "ratio", + "sourceField": "Records" + }, + "d77cdd24-dedc-48dd-9a4b-d34c6f1a6c46": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Product name", + "operationType": "terms", + "params": { + "missingBucket": false, + "orderBy": { + "columnId": "7ac31901-277a-46e2-8128-8d684b2c1127", + "type": "column" + }, + "orderDirection": "desc", + "otherBucket": false, + "size": 5 + }, + "scale": "ordinal", + "sourceField": "products.product_name.keyword" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": false, + "yLeft": true, + "yRight": true + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "layers": [ + { + "accessors": [ + "7ac31901-277a-46e2-8128-8d684b2c1127" + ], + "layerId": "5ed846c2-a70b-4d9c-a244-f254bef763b8", + "layerType": "data", + "position": "top", + "seriesType": "bar_horizontal", + "showGridlines": false, + "xAccessor": "d77cdd24-dedc-48dd-9a4b-d34c6f1a6c46" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "bar_horizontal", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "inside", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "Top products this week", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "03071e90-f5eb-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-5ed846c2-a70b-4d9c-a244-f254bef763b8", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxNiwxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "5ed846c2-a70b-4d9c-a244-f254bef763b8": { + "columnOrder": [ + "d77cdd24-dedc-48dd-9a4b-d34c6f1a6c46", + "7ac31901-277a-46e2-8128-8d684b2c1127" + ], + "columns": { + "7ac31901-277a-46e2-8128-8d684b2c1127": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Items", + "operationType": "count", + "scale": "ratio", + "sourceField": "Records" + }, + "d77cdd24-dedc-48dd-9a4b-d34c6f1a6c46": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Product name", + "operationType": "terms", + "params": { + "missingBucket": false, + "orderBy": { + "columnId": "7ac31901-277a-46e2-8128-8d684b2c1127", + "type": "column" + }, + "orderDirection": "desc", + "otherBucket": false, + "size": 5 + }, + "scale": "ordinal", + "sourceField": "products.product_name.keyword" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": false, + "yLeft": true, + "yRight": true + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "layers": [ + { + "accessors": [ + "7ac31901-277a-46e2-8128-8d684b2c1127" + ], + "layerId": "5ed846c2-a70b-4d9c-a244-f254bef763b8", + "layerType": "data", + "position": "top", + "seriesType": "bar_horizontal", + "showGridlines": false, + "xAccessor": "d77cdd24-dedc-48dd-9a4b-d34c6f1a6c46" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "bar_horizontal", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "inside", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "Top products last week", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "06379e00-f5eb-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-5ed846c2-a70b-4d9c-a244-f254bef763b8", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxNywxXQ==" +} + +{ + "attributes": { + "description": "", + "layerListJSON": "[{\"id\":\"0hmz5\",\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true,\"lightModeDefault\":\"road_map_desaturated\"},\"visible\":true,\"style\":{},\"type\":\"EMS_VECTOR_TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"7ameq\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"world_countries\",\"tooltipProperties\":[\"name\",\"iso2\"]},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"__kbnjoin__count__741db9c6-8ebb-4ea9-9885-b6b4ac019d14\",\"origin\":\"join\"},\"color\":\"Green to Red\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}}},\"type\":\"GEOJSON_VECTOR\",\"joins\":[{\"leftField\":\"iso2\",\"right\":{\"type\":\"ES_TERM_SOURCE\",\"id\":\"741db9c6-8ebb-4ea9-9885-b6b4ac019d14\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.country_iso_code\",\"indexPatternRefName\":\"layer_1_join_0_index_pattern\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"}],\"applyGlobalQuery\":true}}]},{\"id\":\"jmtgf\",\"label\":\"United States\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"usa_states\",\"tooltipProperties\":[\"name\"]},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"__kbnjoin__count__30a0ec24-49b6-476a-b4ed-6c1636333695\",\"origin\":\"join\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}}},\"type\":\"GEOJSON_VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"type\":\"ES_TERM_SOURCE\",\"id\":\"30a0ec24-49b6-476a-b4ed-6c1636333695\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_2_join_0_index_pattern\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"}],\"applyGlobalQuery\":true}}]},{\"id\":\"ui5f8\",\"label\":\"France\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"france_departments\",\"tooltipProperties\":[\"label_en\"]},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"__kbnjoin__count__e325c9da-73fa-4b3b-8b59-364b99370826\",\"origin\":\"join\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}}},\"type\":\"GEOJSON_VECTOR\",\"joins\":[{\"leftField\":\"label_en\",\"right\":{\"type\":\"ES_TERM_SOURCE\",\"id\":\"e325c9da-73fa-4b3b-8b59-364b99370826\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_3_join_0_index_pattern\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"}],\"applyGlobalQuery\":true}}]},{\"id\":\"y3fjb\",\"label\":\"United Kingdom\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"uk_subdivisions\",\"tooltipProperties\":[\"label_en\"]},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"__kbnjoin__count__612d805d-8533-43a9-ac0e-cbf51fe63dcd\",\"origin\":\"join\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}}},\"type\":\"GEOJSON_VECTOR\",\"joins\":[{\"leftField\":\"label_en\",\"right\":{\"type\":\"ES_TERM_SOURCE\",\"id\":\"612d805d-8533-43a9-ac0e-cbf51fe63dcd\",\"indexPatternTitle\":\"kibana_sample_data_ecommerce\",\"term\":\"geoip.region_name\",\"indexPatternRefName\":\"layer_4_join_0_index_pattern\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"}],\"applyGlobalQuery\":true}}]},{\"id\":\"c54wk\",\"label\":\"Sales\",\"minZoom\":9,\"maxZoom\":24,\"alpha\":1,\"sourceDescriptor\":{\"id\":\"04c983b0-8cfa-4e6a-a64b-52c10b7008fe\",\"type\":\"ES_SEARCH\",\"geoField\":\"geoip.location\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[\"category\",\"customer_gender\",\"manufacturer\",\"order_id\",\"total_quantity\",\"total_unique_products\",\"taxful_total_price\",\"order_date\",\"geoip.region_name\",\"geoip.country_iso_code\"],\"indexPatternRefName\":\"layer_5_source_index_pattern\",\"applyGlobalQuery\":true,\"scalingType\":\"LIMIT\"},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"taxful_total_price\",\"origin\":\"source\"},\"color\":\"Greens\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}}},\"type\":\"GEOJSON_VECTOR\"},{\"id\":\"qvhh3\",\"label\":\"Total Sales Revenue\",\"minZoom\":0,\"maxZoom\":9,\"alpha\":1,\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"resolution\":\"COARSE\",\"id\":\"aa7f87b8-9dc5-42be-b19e-1a2fa09b6cad\",\"geoField\":\"geoip.location\",\"requestType\":\"point\",\"metrics\":[{\"type\":\"count\",\"label\":\"sales count\"},{\"type\":\"sum\",\"field\":\"taxful_total_price\",\"label\":\"total sales price\"}],\"indexPatternRefName\":\"layer_6_source_index_pattern\",\"applyGlobalQuery\":true},\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Greens\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"sum_of_taxful_total_price\",\"origin\":\"source\"},\"minSize\":1,\"maxSize\":20,\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"labelText\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"sum_of_taxful_total_price\",\"origin\":\"source\"},\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"labelSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"name\":\"sum_of_taxful_total_price\",\"origin\":\"source\"},\"minSize\":12,\"maxSize\":24,\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"labelBorderSize\":{\"options\":{\"size\":\"MEDIUM\"}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}}},\"type\":\"GEOJSON_VECTOR\"}]", + "mapStateJSON": "{\"zoom\":2.11,\"center\":{\"lon\":-15.07605,\"lat\":45.88578},\"timeFilters\":{\"from\":\"now-7d\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"settings\":{\"autoFitToDataBounds\":false}}", + "title": "[eCommerce] Orders by Country", + "uiStateJSON": "{\"isDarkMode\":false}" + }, + "coreMigrationVersion": "8.1.0", + "id": "2c9c1f60-1909-11e9-919b-ffe5949a18d2", + "migrationVersion": { + "map": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "layer_1_join_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "layer_2_join_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "layer_3_join_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "layer_4_join_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "layer_5_source_index_pattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "layer_6_source_index_pattern", + "type": "index-pattern" + } + ], + "type": "map", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYyMSwxXQ==" +} + +{ + "attributes": { + "columns": [ + "category", + "taxful_total_price", + "products.price", + "products.product_name", + "products.manufacturer", + "sku" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "sort": [ + [ + "order_date", + "desc" + ] + ], + "title": "[eCommerce] Orders", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "3ba638e0-b894-11e8-a6d9-e546fe2bba5f", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYwNSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "title": "[eCommerce] Promotion Tracking", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"[eCommerce] Promotion Tracking\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"ea20ae70-b88d-11e8-a451-f37365e9f268\",\"color\":\"rgba(211,96,134,1)\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"ea20ae71-b88d-11e8-a451-f37365e9f268\",\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":\"2\",\"point_size\":\"5\",\"fill\":\"0\",\"stacked\":\"none\",\"filter\":{\"query\":\"products.product_name:*trouser*\",\"language\":\"lucene\"},\"label\":\"Revenue Trousers\",\"value_template\":\"${{value}}\",\"split_color_mode\":\"gradient\"},{\"id\":\"062d77b0-b88e-11e8-a451-f37365e9f268\",\"color\":\"rgba(84,179,153,1)\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"062d77b1-b88e-11e8-a451-f37365e9f268\",\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":\"2\",\"point_size\":\"05\",\"fill\":\"0\",\"stacked\":\"none\",\"filter\":{\"query\":\"products.product_name:*watch*\",\"language\":\"lucene\"},\"label\":\"Revenue Watches\",\"value_template\":\"${{value}}\",\"split_color_mode\":\"gradient\"},{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"rgba(96,146,192,1)\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":\"2\",\"point_size\":\"5\",\"fill\":\"0\",\"stacked\":\"none\",\"filter\":{\"query\":\"products.product_name:*bag*\",\"language\":\"lucene\"},\"label\":\"Revenue Bags\",\"value_template\":\"${{value}}\",\"split_color_mode\":\"gradient\"},{\"id\":\"faa2c170-b88d-11e8-a451-f37365e9f268\",\"color\":\"rgba(202,142,174,1)\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"faa2c171-b88d-11e8-a451-f37365e9f268\",\"type\":\"sum\",\"field\":\"taxful_total_price\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":\"2\",\"point_size\":\"5\",\"fill\":\"0\",\"stacked\":\"none\",\"filter\":{\"query\":\"products.product_name:*cocktail dress*\",\"language\":\"lucene\"},\"label\":\"Revenue Cocktail Dresses\",\"value_template\":\"${{value}}\",\"split_color_mode\":\"gradient\"}],\"time_field\":\"order_date\",\"interval\":\"12h\",\"use_kibana_indexes\":true,\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"legend_position\":\"bottom\",\"annotations\":[{\"fields\":\"taxful_total_price\",\"template\":\"Ring the bell! ${{taxful_total_price}}\",\"query_string\":{\"query\":\"taxful_total_price:>250\",\"language\":\"lucene\"},\"id\":\"c8c30be0-b88f-11e8-a451-f37365e9f268\",\"color\":\"rgba(25,77,51,1)\",\"time_field\":\"order_date\",\"icon\":\"fa-bell\",\"ignore_global_filters\":1,\"ignore_panel_filters\":1,\"index_pattern_ref_name\":\"metrics_1_index_pattern\"}],\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"isModelInvalid\":false,\"index_pattern_ref_name\":\"metrics_0_index_pattern\"}}" + }, + "coreMigrationVersion": "8.1.0", + "id": "45e07720-b890-11e8-a6d9-e546fe2bba5f", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "metrics_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "metrics_1_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYwMywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "title": "[eCommerce] Sold Products per Day", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"[eCommerce] Sold Products per Day\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"gauge\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"},{\"id\":\"fd1e1b90-e4e3-11eb-8234-cb7bfd534fce\",\"type\":\"math\",\"variables\":[{\"id\":\"00374270-e4e4-11eb-8234-cb7bfd534fce\",\"name\":\"c\",\"field\":\"61ca57f2-469d-11e7-af02-69e470af7417\"}],\"script\":\"params.c / (params._interval / 1000 / 60 / 60 / 24)\"}],\"separate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"0.0\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"label\":\"Trxns / day\",\"split_color_mode\":\"gradient\",\"value_template\":\"\"}],\"time_field\":\"order_date\",\"interval\":\"1d\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"axis_scale\":\"normal\",\"show_legend\":1,\"show_grid\":1,\"gauge_color_rules\":[{\"value\":150,\"id\":\"6da070c0-b891-11e8-b645-195edeb9de84\",\"gauge\":\"rgba(104,188,0,1)\",\"operator\":\"gte\"},{\"value\":150,\"id\":\"9b0cdbc0-b891-11e8-b645-195edeb9de84\",\"gauge\":\"rgba(244,78,59,1)\",\"operator\":\"lt\"}],\"gauge_width\":\"15\",\"gauge_inner_width\":\"10\",\"gauge_style\":\"half\",\"filter\":\"\",\"gauge_max\":\"300\",\"use_kibana_indexes\":true,\"hide_last_value_indicator\":true,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"isModelInvalid\":false,\"index_pattern_ref_name\":\"metrics_0_index_pattern\"}}" + }, + "coreMigrationVersion": "8.1.0", + "id": "b80e6540-b891-11e8-a6d9-e546fe2bba5f", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "metrics_0_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYwNCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "title": "[eCommerce] Markdown", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"[eCommerce] Markdown\",\"type\":\"markdown\",\"params\":{\"fontSize\":12,\"openLinksInNewTab\":false,\"markdown\":\"### Sample eCommerce Data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html).\"},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.1.0", + "id": "c00d1f90-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYwNywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "title": "[eCommerce] Controls", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"[eCommerce] Controls\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1536977437774\",\"fieldName\":\"manufacturer.keyword\",\"parent\":\"\",\"label\":\"Manufacturer\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1536977465554\",\"fieldName\":\"category.keyword\",\"parent\":\"\",\"label\":\"Category\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"dynamicOptions\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"},{\"id\":\"1536977596163\",\"fieldName\":\"total_quantity\",\"parent\":\"\",\"label\":\"Quantity\",\"type\":\"range\",\"options\":{\"decimalPlaces\":0,\"step\":1},\"indexPatternRefName\":\"control_2_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":true,\"pinFilters\":false},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.1.0", + "id": "c3378480-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "control_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "control_1_index_pattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "control_2_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYwOCwxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "c7478794-6767-4286-9d65-1c0ecd909dd8": { + "columnOrder": [ + "8289349e-6d1b-4abf-b164-0208183d2c34", + "041db33b-5c9c-47f3-a5d3-ef5e255d1663", + "041db33b-5c9c-47f3-a5d3-ef5e255d1663X0", + "041db33b-5c9c-47f3-a5d3-ef5e255d1663X1" + ], + "columns": { + "041db33b-5c9c-47f3-a5d3-ef5e255d1663": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "% of target ($10k)", + "operationType": "formula", + "params": { + "format": { + "id": "percent", + "params": { + "decimals": 0 + } + }, + "formula": "sum(taxful_total_price) / 10000 - 1", + "isFormulaBroken": false + }, + "references": [ + "041db33b-5c9c-47f3-a5d3-ef5e255d1663X1" + ], + "scale": "ratio" + }, + "041db33b-5c9c-47f3-a5d3-ef5e255d1663X0": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Part of Weekly revenue", + "operationType": "sum", + "scale": "ratio", + "sourceField": "taxful_total_price" + }, + "041db33b-5c9c-47f3-a5d3-ef5e255d1663X1": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Part of Weekly revenue", + "operationType": "math", + "params": { + "tinymathAst": { + "args": [ + { + "args": [ + "041db33b-5c9c-47f3-a5d3-ef5e255d1663X0", + 10000 + ], + "location": { + "max": 32, + "min": 0 + }, + "name": "divide", + "text": "sum(taxful_total_price) / 10000 ", + "type": "function" + }, + 1 + ], + "location": { + "max": 35, + "min": 0 + }, + "name": "subtract", + "text": "sum(taxful_total_price) / 10000 - 1", + "type": "function" + } + }, + "references": [ + "041db33b-5c9c-47f3-a5d3-ef5e255d1663X0" + ], + "scale": "ratio" + }, + "8289349e-6d1b-4abf-b164-0208183d2c34": { + "dataType": "date", + "isBucketed": true, + "label": "order_date", + "operationType": "date_histogram", + "params": { + "interval": "1d" + }, + "scale": "interval", + "sourceField": "order_date" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": false, + "yLeft": false, + "yRight": true + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "layers": [ + { + "accessors": [ + "041db33b-5c9c-47f3-a5d3-ef5e255d1663" + ], + "layerId": "c7478794-6767-4286-9d65-1c0ecd909dd8", + "layerType": "data", + "seriesType": "bar_stacked", + "xAccessor": "8289349e-6d1b-4abf-b164-0208183d2c34" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "bar_stacked", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "hide", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "% of target revenue ($10k)", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "c762b7a0-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-c7478794-6767-4286-9d65-1c0ecd909dd8", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYwOSwxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "c7478794-6767-4286-9d65-1c0ecd909dd8": { + "columnOrder": [ + "041db33b-5c9c-47f3-a5d3-ef5e255d1663" + ], + "columns": { + "041db33b-5c9c-47f3-a5d3-ef5e255d1663": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Sum of revenue", + "operationType": "sum", + "scale": "ratio", + "sourceField": "taxful_total_price" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "accessor": "041db33b-5c9c-47f3-a5d3-ef5e255d1663", + "layerId": "c7478794-6767-4286-9d65-1c0ecd909dd8", + "layerType": "data" + } + }, + "title": "Sum of revenue", + "visualizationType": "lnsMetric" + }, + "coreMigrationVersion": "8.1.0", + "id": "ce02e260-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-c7478794-6767-4286-9d65-1c0ecd909dd8", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxMCwxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "4fb42a8e-b133-43c8-805c-a38472053938": { + "columnOrder": [ + "020bbfdf-9ef8-4802-aa9e-342d2ea0bebf" + ], + "columns": { + "020bbfdf-9ef8-4802-aa9e-342d2ea0bebf": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Median spending", + "operationType": "median", + "scale": "ratio", + "sourceField": "taxful_total_price" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "accessor": "020bbfdf-9ef8-4802-aa9e-342d2ea0bebf", + "layerId": "4fb42a8e-b133-43c8-805c-a38472053938", + "layerType": "data" + } + }, + "title": "Median spending", + "visualizationType": "lnsMetric" + }, + "coreMigrationVersion": "8.1.0", + "id": "d5f90030-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-4fb42a8e-b133-43c8-805c-a38472053938", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxMSwxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "b6093a53-884f-42c2-9fcc-ba56cfb66c53": { + "columnOrder": [ + "15c45f89-a149-443a-a830-aa8c3a9317db", + "2b41b3d8-2f62-407a-a866-960f254c679d", + "eadae280-2da3-4d1d-a0e1-f9733f89c15b", + "ddc92e50-4d5c-413e-b91b-3e504889fa65", + "5e31e5d3-2aaa-4475-a130-3b69bf2f748a" + ], + "columns": { + "15c45f89-a149-443a-a830-aa8c3a9317db": { + "dataType": "date", + "isBucketed": true, + "label": "order_date", + "operationType": "date_histogram", + "params": { + "interval": "1d" + }, + "scale": "interval", + "sourceField": "order_date" + }, + "2b41b3d8-2f62-407a-a866-960f254c679d": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Total items", + "operationType": "sum", + "scale": "ratio", + "sourceField": "products.quantity" + }, + "5e31e5d3-2aaa-4475-a130-3b69bf2f748a": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Tx. last week", + "operationType": "count", + "scale": "ratio", + "sourceField": "Records", + "timeShift": "1w" + }, + "ddc92e50-4d5c-413e-b91b-3e504889fa65": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Transactions", + "operationType": "count", + "scale": "ratio", + "sourceField": "Records" + }, + "eadae280-2da3-4d1d-a0e1-f9733f89c15b": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Last week", + "operationType": "sum", + "scale": "ratio", + "sourceField": "products.quantity", + "timeShift": "1w" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": false, + "yLeft": false, + "yRight": true + }, + "curveType": "LINEAR", + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "layers": [ + { + "accessors": [ + "2b41b3d8-2f62-407a-a866-960f254c679d", + "eadae280-2da3-4d1d-a0e1-f9733f89c15b", + "5e31e5d3-2aaa-4475-a130-3b69bf2f748a", + "ddc92e50-4d5c-413e-b91b-3e504889fa65" + ], + "layerId": "b6093a53-884f-42c2-9fcc-ba56cfb66c53", + "layerType": "data", + "position": "top", + "seriesType": "line", + "showGridlines": false, + "xAccessor": "15c45f89-a149-443a-a830-aa8c3a9317db", + "yConfig": [ + { + "color": "#b6e0d5", + "forAccessor": "eadae280-2da3-4d1d-a0e1-f9733f89c15b" + }, + { + "color": "#edafc4", + "forAccessor": "5e31e5d3-2aaa-4475-a130-3b69bf2f748a" + } + ] + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "line", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "hide", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "Transactions per day", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "dde978b0-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-b6093a53-884f-42c2-9fcc-ba56cfb66c53", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxMiwxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "667067a2-7cdf-4f0e-a9fe-eb4f4f1f2f17": { + "columnOrder": [ + "c52c2003-ae58-4604-bae7-52ba0fb38a01" + ], + "columns": { + "c52c2003-ae58-4604-bae7-52ba0fb38a01": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Avg. items sold", + "operationType": "average", + "params": { + "format": { + "id": "number", + "params": { + "decimals": 1 + } + } + }, + "scale": "ratio", + "sourceField": "total_quantity" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "accessor": "c52c2003-ae58-4604-bae7-52ba0fb38a01", + "layerId": "667067a2-7cdf-4f0e-a9fe-eb4f4f1f2f17", + "layerType": "data" + } + }, + "title": "Avg. items sold", + "visualizationType": "lnsMetric" + }, + "coreMigrationVersion": "8.1.0", + "id": "e3902840-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-667067a2-7cdf-4f0e-a9fe-eb4f4f1f2f17", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxMywxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "97c63ea6-9305-4755-97d1-0f26817c6f9a": { + "columnOrder": [ + "9f61a7df-198e-4754-b34c-81ed544136ba", + "ebcb19af-0900-4439-949f-d8cd9bccde19", + "5575214b-7f21-4b6c-8bc1-34433c6a0c58" + ], + "columns": { + "5575214b-7f21-4b6c-8bc1-34433c6a0c58": { + "dataType": "number", + "isBucketed": false, + "label": "Count of records", + "operationType": "count", + "scale": "ratio", + "sourceField": "Records" + }, + "9f61a7df-198e-4754-b34c-81ed544136ba": { + "dataType": "string", + "isBucketed": true, + "label": "Top values of category.keyword", + "operationType": "terms", + "params": { + "missingBucket": false, + "orderBy": { + "columnId": "5575214b-7f21-4b6c-8bc1-34433c6a0c58", + "type": "column" + }, + "orderDirection": "desc", + "otherBucket": true, + "size": 10 + }, + "scale": "ordinal", + "sourceField": "category.keyword" + }, + "ebcb19af-0900-4439-949f-d8cd9bccde19": { + "dataType": "date", + "isBucketed": true, + "label": "order_date", + "operationType": "date_histogram", + "params": { + "interval": "1d" + }, + "scale": "interval", + "sourceField": "order_date" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": false, + "yLeft": false, + "yRight": true + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "layers": [ + { + "accessors": [ + "5575214b-7f21-4b6c-8bc1-34433c6a0c58" + ], + "layerId": "97c63ea6-9305-4755-97d1-0f26817c6f9a", + "layerType": "data", + "position": "top", + "seriesType": "bar_percentage_stacked", + "showGridlines": false, + "splitAccessor": "9f61a7df-198e-4754-b34c-81ed544136ba", + "xAccessor": "ebcb19af-0900-4439-949f-d8cd9bccde19" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "bar_percentage_stacked", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "inside", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "Breakdown by category", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "eddf7850-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-97c63ea6-9305-4755-97d1-0f26817c6f9a", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxNCwxXQ==" +} + +{ + "attributes": { + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "0731ee8b-31c5-4be9-92d9-69ee760465d7": { + "columnOrder": [ + "7bf8f089-1542-40bd-b349-45fdfc309ac6", + "826b2f39-b616-40b2-a222-972fdc1d7596", + "cfd45c47-fc41-430c-9e7a-b71dc0c916b0", + "bf51c1af-443e-49f4-a21f-54c87bfc5677", + "bf51c1af-443e-49f4-a21f-54c87bfc5677X0", + "bf51c1af-443e-49f4-a21f-54c87bfc5677X1", + "bf51c1af-443e-49f4-a21f-54c87bfc5677X2" + ], + "columns": { + "7bf8f089-1542-40bd-b349-45fdfc309ac6": { + "dataType": "date", + "isBucketed": true, + "label": "order_date", + "operationType": "date_histogram", + "params": { + "interval": "1d" + }, + "scale": "interval", + "sourceField": "order_date" + }, + "826b2f39-b616-40b2-a222-972fdc1d7596": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "This week", + "operationType": "sum", + "scale": "ratio", + "sourceField": "taxful_total_price" + }, + "bf51c1af-443e-49f4-a21f-54c87bfc5677": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Difference", + "operationType": "formula", + "params": { + "format": { + "id": "number", + "params": { + "decimals": 2 + } + }, + "formula": "sum(taxful_total_price) - sum(taxful_total_price, shift='1w')", + "isFormulaBroken": false + }, + "references": [ + "bf51c1af-443e-49f4-a21f-54c87bfc5677X2" + ], + "scale": "ratio" + }, + "bf51c1af-443e-49f4-a21f-54c87bfc5677X0": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Part of Difference", + "operationType": "sum", + "scale": "ratio", + "sourceField": "taxful_total_price" + }, + "bf51c1af-443e-49f4-a21f-54c87bfc5677X1": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Part of Difference", + "operationType": "sum", + "scale": "ratio", + "sourceField": "taxful_total_price", + "timeShift": "1w" + }, + "bf51c1af-443e-49f4-a21f-54c87bfc5677X2": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "Part of Difference", + "operationType": "math", + "params": { + "tinymathAst": { + "args": [ + "bf51c1af-443e-49f4-a21f-54c87bfc5677X0", + "bf51c1af-443e-49f4-a21f-54c87bfc5677X1" + ], + "location": { + "max": 61, + "min": 0 + }, + "name": "subtract", + "text": "sum(taxful_total_price) - sum(taxful_total_price, shift='1w')", + "type": "function" + } + }, + "references": [ + "bf51c1af-443e-49f4-a21f-54c87bfc5677X0", + "bf51c1af-443e-49f4-a21f-54c87bfc5677X1" + ], + "scale": "ratio" + }, + "cfd45c47-fc41-430c-9e7a-b71dc0c916b0": { + "customLabel": true, + "dataType": "number", + "isBucketed": false, + "label": "1 week ago", + "operationType": "sum", + "scale": "ratio", + "sourceField": "taxful_total_price", + "timeShift": "1w" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "columns": [ + { + "columnId": "7bf8f089-1542-40bd-b349-45fdfc309ac6" + }, + { + "alignment": "left", + "columnId": "826b2f39-b616-40b2-a222-972fdc1d7596" + }, + { + "columnId": "cfd45c47-fc41-430c-9e7a-b71dc0c916b0" + }, + { + "colorMode": "text", + "columnId": "bf51c1af-443e-49f4-a21f-54c87bfc5677", + "isTransposed": false, + "palette": { + "name": "custom", + "params": { + "colorStops": [ + { + "color": "#D36086", + "stop": -10000 + }, + { + "color": "#209280", + "stop": 0 + } + ], + "continuity": "above", + "name": "custom", + "rangeMax": 0, + "rangeMin": -10000, + "rangeType": "number", + "steps": 5, + "stops": [ + { + "color": "#D36086", + "stop": 0 + }, + { + "color": "#209280", + "stop": 2249.03125 + } + ] + }, + "type": "palette" + } + } + ], + "layerId": "0731ee8b-31c5-4be9-92d9-69ee760465d7", + "layerType": "data" + } + }, + "title": "Daily comparison", + "visualizationType": "lnsDatatable" + }, + "coreMigrationVersion": "8.1.0", + "id": "ff6a21b0-f5ea-11eb-a78e-83aac3c38a60", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f", + "name": "indexpattern-datasource-layer-0731ee8b-31c5-4be9-92d9-69ee760465d7", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxNSwxXQ==" +} + +{ + "attributes": { + "description": "Analyze mock eCommerce orders and revenue", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":22,\"w\":24,\"h\":10,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"visualization\",\"gridData\":{\"x\":36,\"y\":15,\"w\":12,\"h\":7,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":55,\"w\":48,\"h\":18,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"map\",\"gridData\":{\"x\":0,\"y\":32,\"w\":24,\"h\":14,\"i\":\"11\"},\"panelIndex\":\"11\",\"embeddableConfig\":{\"isLayerTOCOpen\":false,\"hiddenLayers\":[],\"mapCenter\":{\"lat\":45.88578,\"lon\":-15.07605,\"zoom\":2.11},\"openTOCDetails\":[]},\"panelRefName\":\"panel_11\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":18,\"h\":7,\"i\":\"a71cf076-6895-491c-8878-63592e429ed5\"},\"panelIndex\":\"a71cf076-6895-491c-8878-63592e429ed5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_a71cf076-6895-491c-8878-63592e429ed5\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"visualization\",\"gridData\":{\"x\":18,\"y\":0,\"w\":30,\"h\":7,\"i\":\"adc0a2f4-481c-45eb-b422-0ea59a3e5163\"},\"panelIndex\":\"adc0a2f4-481c-45eb-b422-0ea59a3e5163\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_adc0a2f4-481c-45eb-b422-0ea59a3e5163\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":7,\"w\":24,\"h\":8,\"i\":\"7077b79f-2a99-4fcb-bbd4-456982843278\"},\"panelIndex\":\"7077b79f-2a99-4fcb-bbd4-456982843278\",\"embeddableConfig\":{\"enhancements\":{},\"hidePanelTitles\":false},\"title\":\"% of target revenue ($10k)\",\"panelRefName\":\"panel_7077b79f-2a99-4fcb-bbd4-456982843278\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":7,\"w\":12,\"h\":8,\"i\":\"19a3c101-ad2e-4421-a71b-a4734ec1f03e\"},\"panelIndex\":\"19a3c101-ad2e-4421-a71b-a4734ec1f03e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19a3c101-ad2e-4421-a71b-a4734ec1f03e\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":36,\"y\":7,\"w\":12,\"h\":8,\"i\":\"491469e7-7d24-4216-aeb3-bca00e5c8c1b\"},\"panelIndex\":\"491469e7-7d24-4216-aeb3-bca00e5c8c1b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_491469e7-7d24-4216-aeb3-bca00e5c8c1b\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":7,\"i\":\"a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef\"},\"panelIndex\":\"a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":15,\"w\":12,\"h\":7,\"i\":\"da51079b-952f-43dc-96e6-6f9415a3708b\"},\"panelIndex\":\"da51079b-952f-43dc-96e6-6f9415a3708b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_da51079b-952f-43dc-96e6-6f9415a3708b\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":22,\"w\":24,\"h\":10,\"i\":\"64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b\"},\"panelIndex\":\"64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":32,\"w\":24,\"h\":14,\"i\":\"bd330ede-2eef-4e2a-8100-22a21abf5038\"},\"panelIndex\":\"bd330ede-2eef-4e2a-8100-22a21abf5038\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_bd330ede-2eef-4e2a-8100-22a21abf5038\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":46,\"w\":24,\"h\":9,\"i\":\"b897d4be-cf83-46fb-a111-c7fbec9ef403\"},\"panelIndex\":\"b897d4be-cf83-46fb-a111-c7fbec9ef403\",\"embeddableConfig\":{\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"Top products this week\",\"panelRefName\":\"panel_b897d4be-cf83-46fb-a111-c7fbec9ef403\"},{\"version\":\"8.0.0-SNAPSHOT\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":46,\"w\":24,\"h\":9,\"i\":\"e0f68f93-30f2-4da7-889a-6cd128a68d3f\"},\"panelIndex\":\"e0f68f93-30f2-4da7-889a-6cd128a68d3f\",\"embeddableConfig\":{\"timeRange\":{\"from\":\"now-2w\",\"to\":\"now-1w\"},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"Top products last week\",\"panelRefName\":\"panel_e0f68f93-30f2-4da7-889a-6cd128a68d3f\"}]", + "refreshInterval": { + "pause": true, + "value": 0 + }, + "timeFrom": "now-7d", + "timeRestore": true, + "timeTo": "now", + "title": "[eCommerce] Revenue Dashboard", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "722b74f0-b882-11e8-a6d9-e546fe2bba5f", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "45e07720-b890-11e8-a6d9-e546fe2bba5f", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "b80e6540-b891-11e8-a6d9-e546fe2bba5f", + "name": "7:panel_7", + "type": "visualization" + }, + { + "id": "3ba638e0-b894-11e8-a6d9-e546fe2bba5f", + "name": "10:panel_10", + "type": "search" + }, + { + "id": "2c9c1f60-1909-11e9-919b-ffe5949a18d2", + "name": "11:panel_11", + "type": "map" + }, + { + "id": "c00d1f90-f5ea-11eb-a78e-83aac3c38a60", + "name": "a71cf076-6895-491c-8878-63592e429ed5:panel_a71cf076-6895-491c-8878-63592e429ed5", + "type": "visualization" + }, + { + "id": "c3378480-f5ea-11eb-a78e-83aac3c38a60", + "name": "adc0a2f4-481c-45eb-b422-0ea59a3e5163:panel_adc0a2f4-481c-45eb-b422-0ea59a3e5163", + "type": "visualization" + }, + { + "id": "c762b7a0-f5ea-11eb-a78e-83aac3c38a60", + "name": "7077b79f-2a99-4fcb-bbd4-456982843278:panel_7077b79f-2a99-4fcb-bbd4-456982843278", + "type": "lens" + }, + { + "id": "ce02e260-f5ea-11eb-a78e-83aac3c38a60", + "name": "19a3c101-ad2e-4421-a71b-a4734ec1f03e:panel_19a3c101-ad2e-4421-a71b-a4734ec1f03e", + "type": "lens" + }, + { + "id": "d5f90030-f5ea-11eb-a78e-83aac3c38a60", + "name": "491469e7-7d24-4216-aeb3-bca00e5c8c1b:panel_491469e7-7d24-4216-aeb3-bca00e5c8c1b", + "type": "lens" + }, + { + "id": "dde978b0-f5ea-11eb-a78e-83aac3c38a60", + "name": "a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef:panel_a1b03eb9-a36b-4e12-aa1b-bb29b5d6c4ef", + "type": "lens" + }, + { + "id": "e3902840-f5ea-11eb-a78e-83aac3c38a60", + "name": "da51079b-952f-43dc-96e6-6f9415a3708b:panel_da51079b-952f-43dc-96e6-6f9415a3708b", + "type": "lens" + }, + { + "id": "eddf7850-f5ea-11eb-a78e-83aac3c38a60", + "name": "64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b:panel_64fd5dcf-30c5-4f5a-a78c-70b1fbf87e5b", + "type": "lens" + }, + { + "id": "ff6a21b0-f5ea-11eb-a78e-83aac3c38a60", + "name": "bd330ede-2eef-4e2a-8100-22a21abf5038:panel_bd330ede-2eef-4e2a-8100-22a21abf5038", + "type": "lens" + }, + { + "id": "03071e90-f5eb-11eb-a78e-83aac3c38a60", + "name": "b897d4be-cf83-46fb-a111-c7fbec9ef403:panel_b897d4be-cf83-46fb-a111-c7fbec9ef403", + "type": "lens" + }, + { + "id": "06379e00-f5eb-11eb-a78e-83aac3c38a60", + "name": "e0f68f93-30f2-4da7-889a-6cd128a68d3f:panel_e0f68f93-30f2-4da7-889a-6cd128a68d3f", + "type": "lens" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:42:31.905Z", + "version": "WzYxOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" + }, + "optionsJSON": "{\"useMargins\":true,\"syncColors\":false,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"8.1.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":34,\"i\":\"eb807ec8-cb8c-4195-89a4-5b53bfaeca92\"},\"panelIndex\":\"eb807ec8-cb8c-4195-89a4-5b53bfaeca92\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_eb807ec8-cb8c-4195-89a4-5b53bfaeca92\"}]", + "timeRestore": false, + "title": "Promotion Dashboard", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "d7215b50-7a07-11ec-b0cb-3d2f502358a6", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "45e07720-b890-11e8-a6d9-e546fe2bba5f", + "name": "eb807ec8-cb8c-4195-89a4-5b53bfaeca92:panel_eb807ec8-cb8c-4195-89a4-5b53bfaeca92", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:46:20.289Z", + "version": "WzczMiwxXQ==" +} + +{ + "attributes": { + "allowNoIndex": true, + "timeFieldName": "@timestamp", + "title": "metrics-*" + }, + "coreMigrationVersion": "8.1.0", + "id": "metrics-*", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzI0LDFd" +} + +{ + "attributes": { + "description": "Total events processed by the output (including retries). (From beat.stats.libbeat.output.events.total)", + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "ad65be36-0be3-4937-8f41-ec9e48adfce6": { + "columnOrder": [ + "2e112c50-5bc4-4c0b-a69b-8c17e0f9fc0a", + "49cd060d-6f21-4d81-ad6b-1c8462c97353", + "e201a210-6e89-4d72-9d9c-a00b036fb0eb", + "f5cbe487-2a43-425b-9cd1-40283e5e596c" + ], + "columns": { + "2e112c50-5bc4-4c0b-a69b-8c17e0f9fc0a": { + "dataType": "string", + "isBucketed": true, + "label": "Top values of beat.type", + "operationType": "terms", + "params": { + "missingBucket": false, + "orderBy": { + "fallback": true, + "type": "alphabetical" + }, + "orderDirection": "asc", + "otherBucket": true, + "size": 10 + }, + "scale": "ordinal", + "sourceField": "beat.type" + }, + "49cd060d-6f21-4d81-ad6b-1c8462c97353": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp" + }, + "e201a210-6e89-4d72-9d9c-a00b036fb0eb": { + "customLabel": true, + "dataType": "number", + "filter": { + "language": "kuery", + "query": "data_stream.dataset : \"elastic_agent.*\" " + }, + "isBucketed": false, + "label": "Events Rate /s", + "operationType": "counter_rate", + "references": [ + "f5cbe487-2a43-425b-9cd1-40283e5e596c" + ], + "scale": "ratio", + "timeScale": "s" + }, + "f5cbe487-2a43-425b-9cd1-40283e5e596c": { + "dataType": "number", + "isBucketed": false, + "label": "Maximum of beat.stats.libbeat.output.events.total", + "operationType": "max", + "scale": "ratio", + "sourceField": "beat.stats.libbeat.output.events.total" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": 0 + }, + "layers": [ + { + "accessors": [ + "e201a210-6e89-4d72-9d9c-a00b036fb0eb" + ], + "layerId": "ad65be36-0be3-4937-8f41-ec9e48adfce6", + "layerType": "data", + "position": "top", + "seriesType": "line", + "showGridlines": false, + "splitAccessor": "2e112c50-5bc4-4c0b-a69b-8c17e0f9fc0a", + "xAccessor": "49cd060d-6f21-4d81-ad6b-1c8462c97353" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "line", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "hide", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "[Elastic Agent] Total events rate /s", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-27798780-0037-11ec-af6c-1740f74b2d73", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "metrics-*", + "name": "indexpattern-datasource-layer-ad65be36-0be3-4937-8f41-ec9e48adfce6", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzE3NywxXQ==" +} + +{ + "attributes": { + "description": "Events acknowledged by the output (includes events dropped by the output). (From beat.stats.libbeat.output.events.acked)", + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "ad65be36-0be3-4937-8f41-ec9e48adfce6": { + "columnOrder": [ + "2e112c50-5bc4-4c0b-a69b-8c17e0f9fc0a", + "49cd060d-6f21-4d81-ad6b-1c8462c97353", + "e201a210-6e89-4d72-9d9c-a00b036fb0eb", + "f5cbe487-2a43-425b-9cd1-40283e5e596c" + ], + "columns": { + "2e112c50-5bc4-4c0b-a69b-8c17e0f9fc0a": { + "dataType": "string", + "isBucketed": true, + "label": "Top values of beat.type", + "operationType": "terms", + "params": { + "missingBucket": false, + "orderBy": { + "fallback": true, + "type": "alphabetical" + }, + "orderDirection": "asc", + "otherBucket": true, + "size": 10 + }, + "scale": "ordinal", + "sourceField": "beat.type" + }, + "49cd060d-6f21-4d81-ad6b-1c8462c97353": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp" + }, + "e201a210-6e89-4d72-9d9c-a00b036fb0eb": { + "customLabel": true, + "dataType": "number", + "filter": { + "language": "kuery", + "query": "data_stream.dataset : \"elastic_agent.*\" " + }, + "isBucketed": false, + "label": "Events Rate /s", + "operationType": "counter_rate", + "references": [ + "f5cbe487-2a43-425b-9cd1-40283e5e596c" + ], + "scale": "ratio", + "timeScale": "s" + }, + "f5cbe487-2a43-425b-9cd1-40283e5e596c": { + "dataType": "number", + "isBucketed": false, + "label": "Maximum of beat.stats.libbeat.output.events.acked", + "operationType": "max", + "scale": "ratio", + "sourceField": "beat.stats.libbeat.output.events.acked" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": 0 + }, + "layers": [ + { + "accessors": [ + "e201a210-6e89-4d72-9d9c-a00b036fb0eb" + ], + "layerId": "ad65be36-0be3-4937-8f41-ec9e48adfce6", + "layerType": "data", + "position": "top", + "seriesType": "line", + "showGridlines": false, + "splitAccessor": "2e112c50-5bc4-4c0b-a69b-8c17e0f9fc0a", + "xAccessor": "49cd060d-6f21-4d81-ad6b-1c8462c97353" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "line", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "hide", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "[Elastic Agent] Events acknowledged rate /s", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-409f5d70-0037-11ec-af6c-1740f74b2d73", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "metrics-*", + "name": "indexpattern-datasource-layer-ad65be36-0be3-4937-8f41-ec9e48adfce6", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzE3OCwxXQ==" +} + +{ + "attributes": { + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "[Elastic Agent] Open Handles", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"metrics-*\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"elastic_agent.elastic_agent\\\" \"},\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Open Handles\",\"line_width\":1,\"metrics\":[{\"field\":\"system.process.fd.open\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"max\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"kibana\",\"split_mode\":\"terms\",\"stacked\":\"stacked\",\"terms_field\":\"elastic_agent.process\",\"type\":\"timeseries\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"\",\"tooltip_mode\":\"show_all\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"[Elastic Agent] Open Handles\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-47d87552-8421-4cfc-bc5d-4a7205f5b007", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzQxLDFd" +} + +{ + "attributes": { + "description": "Errors in writing the response from the output. (From beat.stats.libbeat.output.write.errors)", + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "ad65be36-0be3-4937-8f41-ec9e48adfce6": { + "columnOrder": [ + "cb2f461c-587a-4f6a-8ad4-e4b0f61c9541", + "49cd060d-6f21-4d81-ad6b-1c8462c97353", + "e201a210-6e89-4d72-9d9c-a00b036fb0eb", + "f5cbe487-2a43-425b-9cd1-40283e5e596c" + ], + "columns": { + "49cd060d-6f21-4d81-ad6b-1c8462c97353": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp" + }, + "cb2f461c-587a-4f6a-8ad4-e4b0f61c9541": { + "customLabel": true, + "dataType": "string", + "isBucketed": true, + "label": "Beat types", + "operationType": "terms", + "params": { + "missingBucket": false, + "orderBy": { + "fallback": true, + "type": "alphabetical" + }, + "orderDirection": "asc", + "otherBucket": true, + "size": 10 + }, + "scale": "ordinal", + "sourceField": "beat.type" + }, + "e201a210-6e89-4d72-9d9c-a00b036fb0eb": { + "customLabel": true, + "dataType": "number", + "filter": { + "language": "kuery", + "query": "data_stream.dataset : \"elastic_agent.*\" " + }, + "isBucketed": false, + "label": "Output Errors", + "operationType": "counter_rate", + "references": [ + "f5cbe487-2a43-425b-9cd1-40283e5e596c" + ], + "scale": "ratio", + "timeScale": "s" + }, + "f5cbe487-2a43-425b-9cd1-40283e5e596c": { + "dataType": "number", + "isBucketed": false, + "label": "Maximum of beat.stats.libbeat.output.write.errors", + "operationType": "max", + "scale": "ratio", + "sourceField": "beat.stats.libbeat.output.write.errors" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": 0 + }, + "layers": [ + { + "accessors": [ + "e201a210-6e89-4d72-9d9c-a00b036fb0eb" + ], + "layerId": "ad65be36-0be3-4937-8f41-ec9e48adfce6", + "layerType": "data", + "position": "top", + "seriesType": "line", + "showGridlines": false, + "splitAccessor": "cb2f461c-587a-4f6a-8ad4-e4b0f61c9541", + "xAccessor": "49cd060d-6f21-4d81-ad6b-1c8462c97353" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "line", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "hide", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "[Elastic Agent] Output write errors", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-58677820-0037-11ec-af6c-1740f74b2d73", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "metrics-*", + "name": "indexpattern-datasource-layer-ad65be36-0be3-4937-8f41-ec9e48adfce6", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzE3OSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "[Elastic Agent] Memory usage (copy)", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"drop_last_bucket\":0,\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"metrics-*\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":\"0.5\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"elastic_agent.elastic_agent\\\" \"},\"formatter\":\"bytes\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"CGroup Memory usage\",\"line_width\":1,\"metrics\":[{\"field\":\"system.process.cgroup.memory.mem.usage.bytes\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"max\"}],\"override_index_pattern\":0,\"point_size\":1,\"separate_axis\":0,\"series_index_pattern\":\"metrics-*\",\"split_color_mode\":\"kibana\",\"split_mode\":\"terms\",\"stacked\":\"stacked\",\"terms_field\":\"elastic_agent.process\",\"type\":\"timeseries\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(211,96,134,1)\",\"fill\":\"0\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"elastic_agent.elastic_agent\\\" \"},\"formatter\":\"bytes\",\"id\":\"0a454d00-febd-11eb-9943-cf1fa8e46928\",\"label\":\"Container memory limit\",\"line_width\":1,\"metrics\":[{\"field\":\"system.process.cgroup.memory.mem.limit.bytes\",\"id\":\"0a454d01-febd-11eb-9943-cf1fa8e46928\",\"type\":\"max\"},{\"id\":\"53b0dac0-febf-11eb-9943-cf1fa8e46928\",\"script\":\"if (params.memory_limit < 999999999999999999L) {\\n return params.memory_limit;\\n}\\n\",\"type\":\"calculation\",\"variables\":[{\"field\":\"0a454d01-febd-11eb-9943-cf1fa8e46928\",\"id\":\"7426ca80-febf-11eb-9943-cf1fa8e46928\",\"name\":\"memory_limit\"}]}],\"palette\":{\"name\":\"default\",\"type\":\"palette\"},\"point_size\":1,\"separate_axis\":0,\"series_index_pattern\":\"\",\"split_mode\":\"everything\",\"stacked\":\"none\",\"type\":\"timeseries\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"\",\"time_range_mode\":\"entire_time_range\",\"tooltip_mode\":\"show_all\",\"type\":\"timeseries\",\"use_kibana_indexes\":false},\"title\":\"[Elastic Agent] Memory usage (copy)\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-69219f50-febc-11eb-9a5b-19cc90b68e55", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzQyLDFd" +} + +{ + "attributes": { + "description": "Bytes written to the output (consists of size of network headers and compressed payload)", + "state": { + "datasourceStates": { + "indexpattern": { + "layers": { + "47363713-6910-43c5-9f85-328b9ee18f0d": { + "columnOrder": [ + "009f999d-bdb4-4b3f-a031-06d2a7173a57", + "754d7a35-095e-4905-ad7d-23d89edaf74f", + "c601246c-06f3-4f94-9d2a-a950eb4d499e", + "672c59a5-1ad7-4f2b-89a5-cb3920d94e4b" + ], + "columns": { + "009f999d-bdb4-4b3f-a031-06d2a7173a57": { + "dataType": "string", + "isBucketed": true, + "label": "Top values of beat.type", + "operationType": "terms", + "params": { + "missingBucket": false, + "orderBy": { + "fallback": true, + "type": "alphabetical" + }, + "orderDirection": "asc", + "otherBucket": true, + "size": 10 + }, + "scale": "ordinal", + "sourceField": "beat.type" + }, + "672c59a5-1ad7-4f2b-89a5-cb3920d94e4b": { + "dataType": "number", + "isBucketed": false, + "label": "Maximum of beat.stats.libbeat.output.write.bytes", + "operationType": "max", + "scale": "ratio", + "sourceField": "beat.stats.libbeat.output.write.bytes" + }, + "754d7a35-095e-4905-ad7d-23d89edaf74f": { + "dataType": "date", + "isBucketed": true, + "label": "@timestamp", + "operationType": "date_histogram", + "params": { + "interval": "auto" + }, + "scale": "interval", + "sourceField": "@timestamp" + }, + "c601246c-06f3-4f94-9d2a-a950eb4d499e": { + "customLabel": true, + "dataType": "number", + "filter": { + "language": "kuery", + "query": "data_stream.dataset : \"elastic_agent.*\" " + }, + "isBucketed": false, + "label": "Bytes sent/s", + "operationType": "counter_rate", + "params": { + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + } + }, + "references": [ + "672c59a5-1ad7-4f2b-89a5-cb3920d94e4b" + ], + "scale": "ratio", + "timeScale": "s" + } + }, + "incompleteColumns": {} + } + } + } + }, + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "visualization": { + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "fittingFunction": "None", + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": 0 + }, + "layers": [ + { + "accessors": [ + "c601246c-06f3-4f94-9d2a-a950eb4d499e" + ], + "layerId": "47363713-6910-43c5-9f85-328b9ee18f0d", + "layerType": "data", + "position": "top", + "seriesType": "line", + "showGridlines": false, + "splitAccessor": "009f999d-bdb4-4b3f-a031-06d2a7173a57", + "xAccessor": "754d7a35-095e-4905-ad7d-23d89edaf74f" + } + ], + "legend": { + "isVisible": true, + "position": "right" + }, + "preferredSeriesType": "line", + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "valueLabels": "hide", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + } + } + }, + "title": "[Elastic Agent] Output write throughput", + "visualizationType": "lnsXY" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-6e88c0a0-0037-11ec-af6c-1740f74b2d73", + "migrationVersion": { + "lens": "8.1.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "metrics-*", + "name": "indexpattern-datasource-layer-47363713-6910-43c5-9f85-328b9ee18f0d", + "type": "index-pattern" + } + ], + "type": "lens", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzE4MCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "[Elastic Agent] CGroup CPU Usage", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"\"},\"id\":\"f0383b91-4a09-4b03-a013-f5938add6bfa\",\"index_pattern_ref_name\":\"metrics_0_index_pattern\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"elastic_agent.elastic_agent\\\" \"},\"formatter\":\"number\",\"id\":\"a35c4256-5cee-4b6a-ae21-bdd0f0f6d4a2\",\"label\":\"Cgroup CPU usage\",\"line_width\":1,\"metrics\":[{\"field\":\"system.process.cgroup.cpuacct.total.ns\",\"id\":\"458710e3-e78d-4ebf-b9c7-3b1ca8bfc55a\",\"type\":\"max\"},{\"field\":\"system.process.cgroup.cpu.cfs.quota.us\",\"id\":\"5a08b810-fc31-11eb-9d3e-9d72967e3395\",\"type\":\"min\"},{\"field\":\"458710e3-e78d-4ebf-b9c7-3b1ca8bfc55a\",\"id\":\"391dc9f0-fc32-11eb-9d3e-9d72967e3395\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"field\":\"90f31960-fc31-11eb-9d3e-9d72967e3395\",\"id\":\"4661f000-fc32-11eb-9d3e-9d72967e3395\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"field\":\"system.process.cgroup.cpu.stats.periods\",\"id\":\"90f31960-fc31-11eb-9d3e-9d72967e3395\",\"type\":\"max\"},{\"id\":\"5c737680-fc31-11eb-9d3e-9d72967e3395\",\"script\":\"\\n if (params.deltaUsageDerivNormalizedValue > 0 && params.periodsDerivNormalizedValue >0 && params.quota > 0) {\\n // if throttling is configured\\n double factor = params.deltaUsageDerivNormalizedValue / (params.periodsDerivNormalizedValue * params.quota * 1000); \\n\\n return factor * 100; \\n }\\n\\n return null;\",\"type\":\"calculation\",\"variables\":[{\"field\":\"391dc9f0-fc32-11eb-9d3e-9d72967e3395\",\"id\":\"60300950-fc31-11eb-9d3e-9d72967e3395\",\"name\":\"deltaUsageDerivNormalizedValue\"},{\"field\":\"4661f000-fc32-11eb-9d3e-9d72967e3395\",\"id\":\"d6060d50-fc31-11eb-9d3e-9d72967e3395\",\"name\":\"periodsDerivNormalizedValue\"},{\"field\":\"5a08b810-fc31-11eb-9d3e-9d72967e3395\",\"id\":\"e3368450-fc31-11eb-9d3e-9d72967e3395\",\"name\":\"quota\"}]}],\"palette\":{\"name\":\"default\",\"type\":\"palette\"},\"point_size\":1,\"separate_axis\":0,\"split_mode\":\"terms\",\"stacked\":\"stacked\",\"terms_field\":\"elastic_agent.process\",\"type\":\"timeseries\",\"value_template\":\"{{value}}%\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"tooltip_mode\":\"show_all\",\"type\":\"timeseries\",\"use_kibana_indexes\":true},\"title\":\"[Elastic Agent] CGroup CPU Usage\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-819241d0-0037-11ec-af6c-1740f74b2d73", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "metrics_0_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzQzLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "[Elastic Agent] Memory usage", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"metrics-*\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":\"0.5\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"elastic_agent.elastic_agent\\\" \"},\"formatter\":\"bytes\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Memory usage\",\"line_width\":1,\"metrics\":[{\"field\":\"system.process.memory.size\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"max\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"kibana\",\"split_mode\":\"terms\",\"stacked\":\"stacked\",\"terms_field\":\"elastic_agent.process\",\"type\":\"timeseries\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"\",\"tooltip_mode\":\"show_all\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"[Elastic Agent] Memory usage\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-93a8a11d-b2da-4ef3-81dc-c7040560ffde", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzQ0LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "[Elastic Agent] CPU Usage", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"drop_last_bucket\":0,\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"metrics-*\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"elastic_agent.elastic_agent\\\" \"},\"formatter\":\"percent\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"CPU usage\",\"line_width\":1,\"metrics\":[{\"field\":\"system.process.cpu.total.value\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"max\"},{\"field\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"id\":\"42fea6f0-3da7-11eb-a63c-0f13e40aab83\",\"type\":\"derivative\",\"unit\":\"\"},{\"id\":\"48fd6190-3da7-11eb-a63c-0f13e40aab83\",\"script\":\"if (params.cpu_total > 0) {\\n return params.cpu_total / params._interval \\n}\\n\\n\",\"type\":\"calculation\",\"variables\":[{\"field\":\"42fea6f0-3da7-11eb-a63c-0f13e40aab83\",\"id\":\"4b81c280-3da7-11eb-a63c-0f13e40aab83\",\"name\":\"cpu_total\"}]}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"kibana\",\"split_mode\":\"terms\",\"stacked\":\"stacked\",\"terms_field\":\"elastic_agent.process\",\"type\":\"timeseries\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"tooltip_mode\":\"show_all\",\"type\":\"timeseries\",\"use_kibana_indexes\":false},\"title\":\"[Elastic Agent] CPU Usage\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-a11c250a-865f-4eb2-9441-882d229313be", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzQ1LDFd" +} + +{ + "attributes": { + "description": "Elastic Agent metrics dashboard", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"syncColors\":true,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"8.1.0\",\"type\":\"visualization\",\"gridData\":{\"h\":6,\"i\":\"8e715e81-4077-4e7d-9c67-af1d1c98af00\",\"w\":48,\"x\":0,\"y\":0},\"panelIndex\":\"8e715e81-4077-4e7d-9c67-af1d1c98af00\",\"embeddableConfig\":{\"enhancements\":{},\"hidePanelTitles\":false,\"savedVis\":{\"data\":{\"aggs\":[],\"searchSource\":{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"\"}}},\"description\":\"\",\"params\":{\"controls\":[{\"fieldName\":\"host.name\",\"id\":\"1628695092511\",\"indexPatternRefName\":\"control_8e715e81-4077-4e7d-9c67-af1d1c98af00_0_index_pattern\",\"label\":\"Host name\",\"options\":{\"dynamicOptions\":true,\"multiselect\":false,\"order\":\"desc\",\"size\":5,\"type\":\"terms\"},\"parent\":\"\",\"type\":\"list\"}],\"pinFilters\":false,\"updateFiltersOnChange\":false,\"useTimeFilter\":false},\"title\":\"\",\"type\":\"input_control_vis\",\"uiState\":{}},\"type\":\"visualization\"},\"title\":\"Host name\"},{\"embeddableConfig\":{\"enhancements\":{}},\"gridData\":{\"h\":9,\"i\":\"aff03363-b1bf-4d47-9325-3dff44b5e758\",\"w\":24,\"x\":0,\"y\":6},\"panelIndex\":\"aff03363-b1bf-4d47-9325-3dff44b5e758\",\"panelRefName\":\"panel_aff03363-b1bf-4d47-9325-3dff44b5e758\",\"type\":\"visualization\",\"version\":\"7.15.0-SNAPSHOT\"},{\"embeddableConfig\":{\"enhancements\":{}},\"gridData\":{\"h\":9,\"i\":\"5f518ab9-9366-40e5-837b-1b5080d29da3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"5f518ab9-9366-40e5-837b-1b5080d29da3\",\"panelRefName\":\"panel_5f518ab9-9366-40e5-837b-1b5080d29da3\",\"type\":\"visualization\",\"version\":\"7.15.0-SNAPSHOT\"},{\"embeddableConfig\":{\"enhancements\":{},\"hidePanelTitles\":false},\"gridData\":{\"h\":9,\"i\":\"8597b0ac-485c-4749-a2d9-7b8263429ee0\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"8597b0ac-485c-4749-a2d9-7b8263429ee0\",\"panelRefName\":\"panel_8597b0ac-485c-4749-a2d9-7b8263429ee0\",\"title\":\"[Elastic Agent] CGroup Memory usage \",\"type\":\"visualization\",\"version\":\"7.15.0-SNAPSHOT\"},{\"embeddableConfig\":{\"enhancements\":{}},\"gridData\":{\"h\":9,\"i\":\"9ce78b52-e345-4cfd-b2ad-9819e55aaa7a\",\"w\":24,\"x\":0,\"y\":24},\"panelIndex\":\"9ce78b52-e345-4cfd-b2ad-9819e55aaa7a\",\"panelRefName\":\"panel_9ce78b52-e345-4cfd-b2ad-9819e55aaa7a\",\"type\":\"visualization\",\"version\":\"7.15.0-SNAPSHOT\"},{\"embeddableConfig\":{\"enhancements\":{},\"hidePanelTitles\":false},\"gridData\":{\"h\":9,\"i\":\"e58a6da2-e479-4895-a61b-74c3b673c4d9\",\"w\":24,\"x\":0,\"y\":33},\"panelIndex\":\"e58a6da2-e479-4895-a61b-74c3b673c4d9\",\"panelRefName\":\"panel_e58a6da2-e479-4895-a61b-74c3b673c4d9\",\"type\":\"lens\",\"version\":\"7.15.0-SNAPSHOT\"},{\"embeddableConfig\":{\"enhancements\":{},\"hidePanelTitles\":false},\"gridData\":{\"h\":9,\"i\":\"89fea7c1-0908-4710-8b65-1f727f5cab24\",\"w\":24,\"x\":24,\"y\":33},\"panelIndex\":\"89fea7c1-0908-4710-8b65-1f727f5cab24\",\"panelRefName\":\"panel_89fea7c1-0908-4710-8b65-1f727f5cab24\",\"type\":\"lens\",\"version\":\"7.15.0-SNAPSHOT\"},{\"embeddableConfig\":{\"enhancements\":{},\"hidePanelTitles\":false},\"gridData\":{\"h\":9,\"i\":\"b26d8fac-812f-44bf-ad83-acee853b0476\",\"w\":24,\"x\":0,\"y\":42},\"panelIndex\":\"b26d8fac-812f-44bf-ad83-acee853b0476\",\"panelRefName\":\"panel_b26d8fac-812f-44bf-ad83-acee853b0476\",\"title\":\"[Elastic Agent] Errors in writing the response from the output\",\"type\":\"lens\",\"version\":\"7.15.0-SNAPSHOT\"},{\"embeddableConfig\":{\"enhancements\":{},\"hidePanelTitles\":false},\"gridData\":{\"h\":9,\"i\":\"6e45d7b4-8857-448f-8f26-1a63a49d3a78\",\"w\":24,\"x\":24,\"y\":24},\"panelIndex\":\"6e45d7b4-8857-448f-8f26-1a63a49d3a78\",\"panelRefName\":\"panel_6e45d7b4-8857-448f-8f26-1a63a49d3a78\",\"type\":\"lens\",\"version\":\"7.15.0-SNAPSHOT\"},{\"embeddableConfig\":{\"enhancements\":{}},\"gridData\":{\"h\":9,\"i\":\"39247b7d-eb88-4015-b11f-a1105b9fae71\",\"w\":24,\"x\":24,\"y\":6},\"panelIndex\":\"39247b7d-eb88-4015-b11f-a1105b9fae71\",\"panelRefName\":\"panel_39247b7d-eb88-4015-b11f-a1105b9fae71\",\"type\":\"visualization\",\"version\":\"7.15.0-SNAPSHOT\"}]", + "timeRestore": false, + "title": "[Elastic Agent] Agent metrics", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "elastic_agent-f47f18cc-9c7d-4278-b2ea-a6dee816d395", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "metrics-*", + "name": "8e715e81-4077-4e7d-9c67-af1d1c98af00:control_8e715e81-4077-4e7d-9c67-af1d1c98af00_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "elastic_agent-a11c250a-865f-4eb2-9441-882d229313be", + "name": "aff03363-b1bf-4d47-9325-3dff44b5e758:panel_aff03363-b1bf-4d47-9325-3dff44b5e758", + "type": "visualization" + }, + { + "id": "elastic_agent-93a8a11d-b2da-4ef3-81dc-c7040560ffde", + "name": "5f518ab9-9366-40e5-837b-1b5080d29da3:panel_5f518ab9-9366-40e5-837b-1b5080d29da3", + "type": "visualization" + }, + { + "id": "elastic_agent-69219f50-febc-11eb-9a5b-19cc90b68e55", + "name": "8597b0ac-485c-4749-a2d9-7b8263429ee0:panel_8597b0ac-485c-4749-a2d9-7b8263429ee0", + "type": "visualization" + }, + { + "id": "elastic_agent-47d87552-8421-4cfc-bc5d-4a7205f5b007", + "name": "9ce78b52-e345-4cfd-b2ad-9819e55aaa7a:panel_9ce78b52-e345-4cfd-b2ad-9819e55aaa7a", + "type": "visualization" + }, + { + "id": "elastic_agent-27798780-0037-11ec-af6c-1740f74b2d73", + "name": "e58a6da2-e479-4895-a61b-74c3b673c4d9:panel_e58a6da2-e479-4895-a61b-74c3b673c4d9", + "type": "lens" + }, + { + "id": "elastic_agent-409f5d70-0037-11ec-af6c-1740f74b2d73", + "name": "89fea7c1-0908-4710-8b65-1f727f5cab24:panel_89fea7c1-0908-4710-8b65-1f727f5cab24", + "type": "lens" + }, + { + "id": "elastic_agent-58677820-0037-11ec-af6c-1740f74b2d73", + "name": "b26d8fac-812f-44bf-ad83-acee853b0476:panel_b26d8fac-812f-44bf-ad83-acee853b0476", + "type": "lens" + }, + { + "id": "elastic_agent-6e88c0a0-0037-11ec-af6c-1740f74b2d73", + "name": "6e45d7b4-8857-448f-8f26-1a63a49d3a78:panel_6e45d7b4-8857-448f-8f26-1a63a49d3a78", + "type": "lens" + }, + { + "id": "elastic_agent-819241d0-0037-11ec-af6c-1740f74b2d73", + "name": "39247b7d-eb88-4015-b11f-a1105b9fae71:panel_39247b7d-eb88-4015-b11f-a1105b9fae71", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:13.610Z", + "version": "WzI1LDFd" +} + +{ + "attributes": { + "allowNoIndex": true, + "timeFieldName": "@timestamp", + "title": "logs-*" + }, + "coreMigrationVersion": "8.1.0", + "id": "logs-*", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzIzLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4624\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"event.code\":\"4624\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logon Types [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"legendOpen\":true}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"field\":\"winlog.logon.id\"},\"schema\":\"metric\",\"type\":\"cardinality\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"winlog.logon.type\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":20},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"label\":\"user.name: Descending\",\"params\":{}}],\"metric\":{\"accessor\":1,\"aggType\":\"cardinality\",\"format\":{\"id\":\"number\"},\"label\":\"Unique count of winlog.logon.id\",\"params\":{}}},\"isDonut\":false,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"right\",\"type\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"Logon Types [Windows System Security]\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-006d75f0-9c03-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzQ2LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Group Management Events - Description [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":10,\"markdown\":\"# **Group Management Events**\\n\\n#### This dashboard shows information about Group Management Events collected by winlogbeat\\n\",\"openLinksInNewTab\":false},\"title\":\"Group Management Events - Description [Windows System Security]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-6f0f2ea0-f414-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwMiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4731\",\"4727\",\"4754\",\"4744\",\"4759\",\"4779\",\"4790\",\"4783\"],\"type\":\"phrases\",\"value\":\"4731, 4727, 4754, 4744, 4759, 4779, 4790, 4783\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4731\"}},{\"match_phrase\":{\"event.code\":\"4727\"}},{\"match_phrase\":{\"event.code\":\"4754\"}},{\"match_phrase\":{\"event.code\":\"4744\"}},{\"match_phrase\":{\"event.code\":\"4759\"}},{\"match_phrase\":{\"event.code\":\"4779\"}},{\"match_phrase\":{\"event.code\":\"4790\"}},{\"match_phrase\":{\"event.code\":\"4783\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Groups Created - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Group\",\"field\":\"group.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":20},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Domain\",\"field\":\"group.domain\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"Performer LogonID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":3,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":4,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":5,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Groups Created - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-98884120-f49d-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyMCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4735\",\"4737\",\"4755\",\"4750\",\"4760\",\"4745\",\"4791\",\"4784\",\"4764\"],\"type\":\"phrases\",\"value\":\"4735, 4737, 4755, 4750, 4760, 4745, 4791, 4784, 4764\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4735\"}},{\"match_phrase\":{\"event.code\":\"4737\"}},{\"match_phrase\":{\"event.code\":\"4755\"}},{\"match_phrase\":{\"event.code\":\"4750\"}},{\"match_phrase\":{\"event.code\":\"4760\"}},{\"match_phrase\":{\"event.code\":\"4745\"}},{\"match_phrase\":{\"event.code\":\"4791\"}},{\"match_phrase\":{\"event.code\":\"4784\"}},{\"match_phrase\":{\"event.code\":\"4764\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Group Changes - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Group\",\"field\":\"group.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":20},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Domain\",\"field\":\"group.domain\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"Performer LogonID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":3,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":4,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":5,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Group Changes - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-9e534190-f49d-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyMywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4734\",\"4730\",\"4758\",\"4748\",\"4763\",\"4753\",\"4792\",\"4789\"],\"type\":\"phrases\",\"value\":\"4734, 4730, 4758, 4748, 4763, 4753, 4792, 4789\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4734\"}},{\"match_phrase\":{\"event.code\":\"4730\"}},{\"match_phrase\":{\"event.code\":\"4758\"}},{\"match_phrase\":{\"event.code\":\"4748\"}},{\"match_phrase\":{\"event.code\":\"4763\"}},{\"match_phrase\":{\"event.code\":\"4753\"}},{\"match_phrase\":{\"event.code\":\"4792\"}},{\"match_phrase\":{\"event.code\":\"4789\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Groups Deleted - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Group\",\"field\":\"group.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":20},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Domain\",\"field\":\"group.domain\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"Performer LogonID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":3,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":4,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":5,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Groups Deleted - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-bb9cf7a0-f49d-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0NCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4732\",\"4728\",\"4756\",\"4751\",\"4761\",\"4746\",\"4785\",\"4787\"],\"type\":\"phrases\",\"value\":\"4732, 4728, 4756, 4751, 4761, 4746, 4785, 4787\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4732\"}},{\"match_phrase\":{\"event.code\":\"4728\"}},{\"match_phrase\":{\"event.code\":\"4756\"}},{\"match_phrase\":{\"event.code\":\"4751\"}},{\"match_phrase\":{\"event.code\":\"4761\"}},{\"match_phrase\":{\"event.code\":\"4746\"}},{\"match_phrase\":{\"event.code\":\"4785\"}},{\"match_phrase\":{\"event.code\":\"4787\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Added - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"User\",\"field\":\"winlog.event_data.MemberName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Group\",\"field\":\"group.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":10},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Domain\",\"field\":\"group.domain\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"user.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"6\",\"params\":{\"customLabel\":\"Performed by Logon ID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":3,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":4,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":5,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":5,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Added - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-ce867840-f49e-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1NCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4733\",\"4729\",\"4757\",\"4786\",\"4788\",\"4752\",\"4762\",\"4747\"],\"type\":\"phrases\",\"value\":\"4733, 4729, 4757, 4786, 4788, 4752, 4762, 4747\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4733\"}},{\"match_phrase\":{\"event.code\":\"4729\"}},{\"match_phrase\":{\"event.code\":\"4757\"}},{\"match_phrase\":{\"event.code\":\"4786\"}},{\"match_phrase\":{\"event.code\":\"4788\"}},{\"match_phrase\":{\"event.code\":\"4752\"}},{\"match_phrase\":{\"event.code\":\"4762\"}},{\"match_phrase\":{\"event.code\":\"4747\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Removed from Group - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"User\",\"field\":\"winlog.event_data.MemberName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Group\",\"field\":\"group.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":10},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Domain\",\"field\":\"group.domain\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"6\",\"params\":{\"customLabel\":\"Performed by Logon ID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":3,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":4,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":5,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":5,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Removed from Group - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-fee83900-f49f-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE3NSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4799\"],\"type\":\"phrases\",\"value\":\"4799\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4799\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Group Enumeration - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Group\",\"field\":\"group.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":20},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Domain\",\"field\":\"group.domain\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Creator\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"Creator LogonID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":3,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":4,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":5,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Group Enumeration - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-bc165210-f4b8-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0NSwxXQ==" +} + +{ + "attributes": { + "columns": [ + "user.name", + "source.domain", + "source.ip", + "winlog.logon.id", + "winlog.logon.type" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4624\"],\"type\":\"phrases\",\"value\":\"4624\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4624\"}}]}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Logon Details [Windows System Security]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-7e178c80-fee1-11e9-8405-516218e3d268", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4732\",\"4728\",\"4756\",\"4751\",\"4761\",\"4746\",\"4785\",\"4787\"],\"type\":\"phrases\",\"value\":\"4732, 4728, 4756, 4751, 4761, 4746, 4785, 4787\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4732\"}},{\"match_phrase\":{\"event.code\":\"4728\"}},{\"match_phrase\":{\"event.code\":\"4756\"}},{\"match_phrase\":{\"event.code\":\"4751\"}},{\"match_phrase\":{\"event.code\":\"4761\"}},{\"match_phrase\":{\"event.code\":\"4746\"}},{\"match_phrase\":{\"event.code\":\"4785\"}},{\"match_phrase\":{\"event.code\":\"4787\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Added - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Users Added to Groups\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Reds\",\"colorsRange\":[{\"from\":0,\"to\":1,\"type\":\"range\"},{\"from\":1,\"to\":5},{\"from\":5,\"to\":10},{\"from\":10,\"to\":15},{\"from\":15,\"to\":20},{\"from\":20,\"to\":9999}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"Labels\",\"percentageMode\":false,\"style\":{\"bgColor\":true,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Added - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-a13bf640-fee8-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzMiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4734\",\"4730\",\"4758\",\"4748\",\"4763\",\"4753\",\"4792\",\"4789\"],\"type\":\"phrases\",\"value\":\"4734, 4730, 4758, 4748, 4763, 4753, 4792, 4789\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4734\"}},{\"match_phrase\":{\"event.code\":\"4730\"}},{\"match_phrase\":{\"event.code\":\"4758\"}},{\"match_phrase\":{\"event.code\":\"4748\"}},{\"match_phrase\":{\"event.code\":\"4763\"}},{\"match_phrase\":{\"event.code\":\"4753\"}},{\"match_phrase\":{\"event.code\":\"4792\"}},{\"match_phrase\":{\"event.code\":\"4789\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"lucene\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Groups Deleted- Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Groups Deleted\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Greens\",\"colorsRange\":[{\"from\":0,\"to\":1,\"type\":\"range\"},{\"from\":1,\"to\":5},{\"from\":5,\"to\":10},{\"from\":10,\"to\":15},{\"from\":15,\"to\":20},{\"from\":20,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"Labels\",\"percentageMode\":false,\"style\":{\"bgColor\":true,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Groups Deleted- Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5eeaafd0-fee7-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzk5LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4731\",\"4727\",\"4754\",\"4744\",\"4759\",\"4779\",\"4790\",\"4783\"],\"type\":\"phrases\",\"value\":\"4731, 4727, 4754, 4744, 4759, 4779, 4790, 4783\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4731\"}},{\"match_phrase\":{\"event.code\":\"4727\"}},{\"match_phrase\":{\"event.code\":\"4754\"}},{\"match_phrase\":{\"event.code\":\"4744\"}},{\"match_phrase\":{\"event.code\":\"4759\"}},{\"match_phrase\":{\"event.code\":\"4779\"}},{\"match_phrase\":{\"event.code\":\"4790\"}},{\"match_phrase\":{\"event.code\":\"4783\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Groups Created - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Groups Created\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Reds\",\"colorsRange\":[{\"from\":0,\"to\":1,\"type\":\"range\"},{\"from\":1,\"to\":10},{\"from\":10,\"to\":20},{\"from\":20,\"to\":9999}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"Labels\",\"percentageMode\":false,\"style\":{\"bgColor\":true,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Groups Created - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-f42f3b20-fee6-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE3MiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4735\",\"4737\",\"4755\",\"4750\",\"4760\",\"4745\",\"4791\",\"4784\",\"4764\"],\"type\":\"phrases\",\"value\":\"4735, 4737, 4755, 4750, 4760, 4745, 4791, 4784, 4764\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4735\"}},{\"match_phrase\":{\"event.code\":\"4737\"}},{\"match_phrase\":{\"event.code\":\"4755\"}},{\"match_phrase\":{\"event.code\":\"4750\"}},{\"match_phrase\":{\"event.code\":\"4760\"}},{\"match_phrase\":{\"event.code\":\"4745\"}},{\"match_phrase\":{\"event.code\":\"4791\"}},{\"match_phrase\":{\"event.code\":\"4784\"}},{\"match_phrase\":{\"event.code\":\"4764\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Groups Changes - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Groups Changed\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Yellow to Red\",\"colorsRange\":[{\"from\":0,\"to\":1,\"type\":\"range\"},{\"from\":1,\"to\":5},{\"from\":5,\"to\":10},{\"from\":10,\"to\":15},{\"from\":15,\"to\":20},{\"from\":20,\"to\":100000}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"Labels\",\"percentageMode\":false,\"style\":{\"bgColor\":true,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Groups Changes - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-b5f38780-fee6-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0MiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4733\",\"4729\",\"4757\",\"4786\",\"4788\",\"4752\",\"4762\",\"4747\"],\"type\":\"phrases\",\"value\":\"4733, 4729, 4757, 4786, 4788, 4752, 4762, 4747\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4733\"}},{\"match_phrase\":{\"event.code\":\"4729\"}},{\"match_phrase\":{\"event.code\":\"4757\"}},{\"match_phrase\":{\"event.code\":\"4786\"}},{\"match_phrase\":{\"event.code\":\"4788\"}},{\"match_phrase\":{\"event.code\":\"4752\"}},{\"match_phrase\":{\"event.code\":\"4762\"}},{\"match_phrase\":{\"event.code\":\"4747\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Removed from Group - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Users Removed from Groups\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Greens\",\"colorsRange\":[{\"from\":0,\"to\":1,\"type\":\"range\"},{\"from\":1,\"to\":5},{\"from\":5,\"to\":9},{\"from\":9,\"to\":13},{\"from\":13,\"to\":17},{\"from\":17,\"to\":20000}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"Labels\",\"percentageMode\":false,\"style\":{\"bgColor\":true,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Removed from Group - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-1b5f17d0-feea-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzYwLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4799\"},\"type\":\"phrase\",\"value\":\"4799\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4799\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Group Membership Enumeration - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Group Membership Enumerated\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Blues\",\"colorsRange\":[{\"from\":0,\"to\":500,\"type\":\"range\"},{\"from\":500,\"to\":20000},{\"from\":20000,\"to\":30000},{\"from\":30000,\"to\":40000}],\"invertColors\":true,\"labels\":{\"show\":true},\"metricColorMode\":\"Labels\",\"percentageMode\":false,\"style\":{\"bgColor\":true,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Group Membership Enumeration - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-0f2f5280-feeb-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzUxLDFd" +} + +{ + "attributes": { + "columns": [ + "event.action", + "group.name", + "group.domain", + "user.name", + "user.domain", + "host.name" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4731\",\"4732\",\"4733\",\"4734\",\"4735\",\"4737\",\"4764\",\"4727\",\"4728\",\"4729\",\"4730\",\"4754\",\"4755\",\"4756\",\"4757\",\"4758\",\"4799\",\"4749\",\"4750\",\"4751\",\"4752\",\"4753\",\"4759\",\"4760\",\"4761\",\"4762\",\"4763\",\"4744\",\"4745\",\"4746\",\"4748\"],\"type\":\"phrases\",\"value\":\"4731, 4732, 4733, 4734, 4735, 4737, 4764, 4727, 4728, 4729, 4730, 4754, 4755, 4756, 4757, 4758, 4799, 4749, 4750, 4751, 4752, 4753, 4759, 4760, 4761, 4762, 4763, 4744, 4745, 4746, 4748\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4731\"}},{\"match_phrase\":{\"event.code\":\"4732\"}},{\"match_phrase\":{\"event.code\":\"4733\"}},{\"match_phrase\":{\"event.code\":\"4734\"}},{\"match_phrase\":{\"event.code\":\"4735\"}},{\"match_phrase\":{\"event.code\":\"4737\"}},{\"match_phrase\":{\"event.code\":\"4764\"}},{\"match_phrase\":{\"event.code\":\"4727\"}},{\"match_phrase\":{\"event.code\":\"4728\"}},{\"match_phrase\":{\"event.code\":\"4729\"}},{\"match_phrase\":{\"event.code\":\"4730\"}},{\"match_phrase\":{\"event.code\":\"4754\"}},{\"match_phrase\":{\"event.code\":\"4755\"}},{\"match_phrase\":{\"event.code\":\"4756\"}},{\"match_phrase\":{\"event.code\":\"4757\"}},{\"match_phrase\":{\"event.code\":\"4758\"}},{\"match_phrase\":{\"event.code\":\"4799\"}},{\"match_phrase\":{\"event.code\":\"4749\"}},{\"match_phrase\":{\"event.code\":\"4750\"}},{\"match_phrase\":{\"event.code\":\"4751\"}},{\"match_phrase\":{\"event.code\":\"4752\"}},{\"match_phrase\":{\"event.code\":\"4753\"}},{\"match_phrase\":{\"event.code\":\"4759\"}},{\"match_phrase\":{\"event.code\":\"4760\"}},{\"match_phrase\":{\"event.code\":\"4761\"}},{\"match_phrase\":{\"event.code\":\"4762\"}},{\"match_phrase\":{\"event.code\":\"4763\"}},{\"match_phrase\":{\"event.code\":\"4744\"}},{\"match_phrase\":{\"event.code\":\"4745\"}},{\"match_phrase\":{\"event.code\":\"4746\"}},{\"match_phrase\":{\"event.code\":\"4748\"}}]}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Group Management Details - Search View [Windows System Security]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-9066d5b0-fef2-11e9-8405-516218e3d268", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4OCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"(data_stream.dataset:windows.application OR data_stream.dataset:windows.forwarded OR data_stream.dataset:windows.powershell OR data_stream.dataset:windows.powershell_operational OR data_stream.dataset:windows.security OR data_stream.dataset:windows.sysmon_operational OR data_stream.dataset:windows.system OR data_stream.dataset:system.application OR data_stream.dataset:system.security OR data_stream.dataset:system.system)\"}}" + }, + "title": "Dashboard links - Simple [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":12,\"markdown\":\"[Windows Overview](#/dashboard/system-Windows-Dashboard) | [User Logon Information](#/dashboard/system-bae11b00-9bfc-11ea-87e4-49f31ec44891) | [Logon Failed and Account Lockout](#/dashboard/system-d401ef40-a7d5-11e9-a422-d144027429da) | [User Management Events](#/dashboard/system-71f720f0-ff18-11e9-8405-516218e3d268) | [Group Management Events](#/dashboard/system-bb858830-f412-11e9-8405-516218e3d268)\",\"openLinksInNewTab\":false},\"title\":\"Dashboard links - Simple [Windows System Security]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-d770b040-9b35-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2MCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Group Management Events - Event Actions - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"event.action\",\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":50},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"event.code\",\"field\":\"event.code\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"perPage\":10,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Group Management Events - Event Actions - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-33462600-9b47-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-9066d5b0-fef2-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzcyLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Group Management Events - Target Groups - Tag Cloud [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"group.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"maxFontSize\":58,\"minFontSize\":18,\"orientation\":\"single\",\"scale\":\"linear\",\"showLabel\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"title\":\"Group Management Events - Target Groups - Tag Cloud [Windows System Security]\",\"type\":\"tagcloud\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-58fb9480-9b46-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-9066d5b0-fef2-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzg5LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Group Management Events - Groups vs Actions - Heatmap [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Target Groups\",\"field\":\"group.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":20},\"schema\":\"segment\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Actions\",\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"colorSchema\":\"Blues\",\"colorsNumber\":4,\"colorsRange\":[],\"enableHover\":false,\"invertColors\":false,\"legendPosition\":\"right\",\"percentageMode\":false,\"setColorRange\":false,\"times\":[],\"type\":\"heatmap\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"color\":\"black\",\"overwriteColor\":false,\"rotate\":0,\"show\":true},\"scale\":{\"defaultYExtents\":false,\"type\":\"linear\"},\"show\":false,\"type\":\"value\"}]},\"title\":\"Group Management Events - Groups vs Actions - Heatmap [Windows System Security]\",\"type\":\"heatmap\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-e20c02d0-9b48-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-9066d5b0-fef2-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Group Management Action Distribution over Time [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"drop_partials\":false,\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"scaleMetricValues\":false,\"timeRange\":{\"from\":\"now-30d\",\"to\":\"now\"},\"useNormalizedEsInterval\":true},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":25},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"filter\":true,\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"valueAxis\":\"\"},\"labels\":{\"show\":false},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"mode\":\"stacked\",\"show\":true,\"showCircles\":true,\"type\":\"histogram\",\"valueAxis\":\"ValueAxis-1\",\"circlesRadius\":1}],\"thresholdLine\":{\"color\":\"#E7664C\",\"show\":false,\"style\":\"full\",\"value\":10,\"width\":1},\"times\":[],\"type\":\"histogram\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"title\":\"Group Management Action Distribution over Time [Windows System Security]\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-7de2e3f0-9b4d-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-9066d5b0-fef2-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Group Management Events - Event Actions [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":false,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"right\",\"type\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"Group Management Events - Event Actions [Windows System Security]\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-b89b0c90-9b41-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-9066d5b0-fef2-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0MywxXQ==" +} + +{ + "attributes": { + "description": "Group management activity with TSVB metrics.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"22\",\"w\":17,\"x\":0,\"y\":0},\"panelIndex\":\"22\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_22\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"36\",\"w\":9,\"x\":0,\"y\":59},\"panelIndex\":\"36\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Creation Summary [Windows System Security]\",\"panelRefName\":\"panel_36\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"37\",\"w\":9,\"x\":9,\"y\":59},\"panelIndex\":\"37\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Changes Summary [Windows System Security]\",\"panelRefName\":\"panel_37\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"38\",\"w\":9,\"x\":18,\"y\":59},\"panelIndex\":\"38\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Deletion Summary [Windows System Security]\",\"panelRefName\":\"panel_38\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":14,\"i\":\"39\",\"w\":16,\"x\":0,\"y\":81},\"panelIndex\":\"39\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Users Added to Group Summary [Windows System Security]\",\"panelRefName\":\"panel_39\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":14,\"i\":\"40\",\"w\":17,\"x\":16,\"y\":81},\"panelIndex\":\"40\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Users Removed From Group Summary [Windows System Security]\",\"panelRefName\":\"panel_40\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":14,\"i\":\"42\",\"w\":15,\"x\":33,\"y\":81},\"panelIndex\":\"42\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Membership Enumeration Summary [Windows System Security]\",\"panelRefName\":\"panel_42\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":22,\"i\":\"43\",\"w\":21,\"x\":27,\"y\":50},\"panelIndex\":\"43\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Details [Windows System Security]\",\"panelRefName\":\"panel_43\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"44\",\"w\":16,\"x\":0,\"y\":72},\"panelIndex\":\"44\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_44\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"45\",\"w\":9,\"x\":18,\"y\":50},\"panelIndex\":\"45\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_45\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"46\",\"w\":9,\"x\":0,\"y\":50},\"panelIndex\":\"46\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_46\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"47\",\"w\":9,\"x\":9,\"y\":50},\"panelIndex\":\"47\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_47\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"48\",\"w\":17,\"x\":16,\"y\":72},\"panelIndex\":\"48\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_48\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"49\",\"w\":15,\"x\":33,\"y\":72},\"panelIndex\":\"49\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_49\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":21,\"i\":\"51\",\"w\":48,\"x\":0,\"y\":95},\"panelIndex\":\"51\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_51\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"45614e1c-b2bb-4243-9a74-a4bdd0124c87\",\"w\":31,\"x\":17,\"y\":0},\"panelIndex\":\"45614e1c-b2bb-4243-9a74-a4bdd0124c87\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_45614e1c-b2bb-4243-9a74-a4bdd0124c87\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"88e75800-8125-4c9e-96b8-5c36f6e91664\",\"w\":9,\"x\":21,\"y\":8},\"panelIndex\":\"88e75800-8125-4c9e-96b8-5c36f6e91664\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_88e75800-8125-4c9e-96b8-5c36f6e91664\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"4b793b8e-72d4-42a2-b377-1c70f0307414\",\"w\":18,\"x\":30,\"y\":8},\"panelIndex\":\"4b793b8e-72d4-42a2-b377-1c70f0307414\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4b793b8e-72d4-42a2-b377-1c70f0307414\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"82d229f9-44f4-4c4b-baf7-f9673a14c87f\",\"w\":26,\"x\":0,\"y\":29},\"panelIndex\":\"82d229f9-44f4-4c4b-baf7-f9673a14c87f\",\"embeddableConfig\":{\"vis\":null,\"enhancements\":{}},\"panelRefName\":\"panel_82d229f9-44f4-4c4b-baf7-f9673a14c87f\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"f44255b0-d9a8-479f-be3f-829c1f6ed794\",\"w\":22,\"x\":26,\"y\":29},\"panelIndex\":\"f44255b0-d9a8-479f-be3f-829c1f6ed794\",\"embeddableConfig\":{\"colors\":{\"added-group-account\":\"#1F78C1\",\"added-member-to-group\":\"#0A437C\",\"deleted-group-account\":\"#5195CE\",\"modified-group-account\":\"#0A50A1\",\"type-changed-group-account\":\"#82B5D8\",\"user-member-enumerated\":\"#2F575E\"},\"vis\":{\"colors\":{\"added-group-account\":\"#1F78C1\",\"added-member-to-group\":\"#0A437C\",\"deleted-group-account\":\"#5195CE\",\"modified-group-account\":\"#0A50A1\",\"removed-member-from-group\":\"#82B5D8\",\"type-changed-group-account\":\"#82B5D8\",\"user-member-enumerated\":\"#2F575E\"}},\"enhancements\":{}},\"panelRefName\":\"panel_f44255b0-d9a8-479f-be3f-829c1f6ed794\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"9c42bff2-b295-4617-8d8c-455bd5948b66\",\"w\":21,\"x\":0,\"y\":8},\"panelIndex\":\"9c42bff2-b295-4617-8d8c-455bd5948b66\",\"embeddableConfig\":{\"colors\":{\"added-group-account\":\"#0A50A1\",\"added-member-to-group\":\"#1F78C1\",\"deleted-group-account\":\"#5195CE\",\"modified-group-account\":\"#0A437C\",\"user-member-enumerated\":\"#052B51\"},\"vis\":{\"colors\":{\"added-group-account\":\"#0A50A1\",\"added-member-to-group\":\"#1F78C1\",\"deleted-group-account\":\"#5195CE\",\"modified-group-account\":\"#0A437C\",\"user-member-enumerated\":\"#2F575E\"}},\"enhancements\":{}},\"panelRefName\":\"panel_9c42bff2-b295-4617-8d8c-455bd5948b66\"}]", + "timeRestore": false, + "title": "[System Windows Security] Group Management Events - Simple Metrics", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-01c54730-fee6-11e9-8405-516218e3d268", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-6f0f2ea0-f414-11e9-8405-516218e3d268", + "name": "22:panel_22", + "type": "visualization" + }, + { + "id": "system-98884120-f49d-11e9-8405-516218e3d268", + "name": "36:panel_36", + "type": "visualization" + }, + { + "id": "system-9e534190-f49d-11e9-8405-516218e3d268", + "name": "37:panel_37", + "type": "visualization" + }, + { + "id": "system-bb9cf7a0-f49d-11e9-8405-516218e3d268", + "name": "38:panel_38", + "type": "visualization" + }, + { + "id": "system-ce867840-f49e-11e9-8405-516218e3d268", + "name": "39:panel_39", + "type": "visualization" + }, + { + "id": "system-fee83900-f49f-11e9-8405-516218e3d268", + "name": "40:panel_40", + "type": "visualization" + }, + { + "id": "system-bc165210-f4b8-11e9-8405-516218e3d268", + "name": "42:panel_42", + "type": "visualization" + }, + { + "id": "system-7e178c80-fee1-11e9-8405-516218e3d268", + "name": "43:panel_43", + "type": "search" + }, + { + "id": "system-a13bf640-fee8-11e9-8405-516218e3d268", + "name": "44:panel_44", + "type": "visualization" + }, + { + "id": "system-5eeaafd0-fee7-11e9-8405-516218e3d268", + "name": "45:panel_45", + "type": "visualization" + }, + { + "id": "system-f42f3b20-fee6-11e9-8405-516218e3d268", + "name": "46:panel_46", + "type": "visualization" + }, + { + "id": "system-b5f38780-fee6-11e9-8405-516218e3d268", + "name": "47:panel_47", + "type": "visualization" + }, + { + "id": "system-1b5f17d0-feea-11e9-8405-516218e3d268", + "name": "48:panel_48", + "type": "visualization" + }, + { + "id": "system-0f2f5280-feeb-11e9-8405-516218e3d268", + "name": "49:panel_49", + "type": "visualization" + }, + { + "id": "system-9066d5b0-fef2-11e9-8405-516218e3d268", + "name": "51:panel_51", + "type": "search" + }, + { + "id": "system-d770b040-9b35-11ea-87e4-49f31ec44891", + "name": "45614e1c-b2bb-4243-9a74-a4bdd0124c87:panel_45614e1c-b2bb-4243-9a74-a4bdd0124c87", + "type": "visualization" + }, + { + "id": "system-33462600-9b47-11ea-87e4-49f31ec44891", + "name": "88e75800-8125-4c9e-96b8-5c36f6e91664:panel_88e75800-8125-4c9e-96b8-5c36f6e91664", + "type": "visualization" + }, + { + "id": "system-58fb9480-9b46-11ea-87e4-49f31ec44891", + "name": "4b793b8e-72d4-42a2-b377-1c70f0307414:panel_4b793b8e-72d4-42a2-b377-1c70f0307414", + "type": "visualization" + }, + { + "id": "system-e20c02d0-9b48-11ea-87e4-49f31ec44891", + "name": "82d229f9-44f4-4c4b-baf7-f9673a14c87f:panel_82d229f9-44f4-4c4b-baf7-f9673a14c87f", + "type": "visualization" + }, + { + "id": "system-7de2e3f0-9b4d-11ea-87e4-49f31ec44891", + "name": "f44255b0-d9a8-479f-be3f-829c1f6ed794:panel_f44255b0-d9a8-479f-be3f-829c1f6ed794", + "type": "visualization" + }, + { + "id": "system-b89b0c90-9b41-11ea-87e4-49f31ec44891", + "name": "9c42bff2-b295-4617-8d8c-455bd5948b66:panel_9c42bff2-b295-4617-8d8c-455bd5948b66", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzI2LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4672\"],\"type\":\"phrases\",\"value\":\"4672\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4672\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logged on Administrators [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"\"},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Date\",\"drop_partials\":false,\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"scaleMetricValues\":false,\"timeRange\":{\"from\":\"2020-05-20T07:35:27.496Z\",\"to\":\"2020-05-22T00:01:10.239Z\"},\"useNormalizedEsInterval\":true},\"schema\":\"bucket\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"6\",\"params\":{\"customLabel\":\"user.name\",\"field\":\"user.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"8\",\"params\":{\"customLabel\":\"# Thread\",\"field\":\"winlog.process.thread.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"9\",\"params\":{\"customLabel\":\"LogonID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"date_histogram\",\"format\":{\"id\":\"date\",\"params\":{\"pattern\":\"YYYY-MM-DD HH:mm\"}},\"label\":\"Fecha - Hora \",\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"label\":\"Usuario\",\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"number\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"label\":\"# Thread\",\"params\":{}},{\"accessor\":3,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"label\":\"winlog.logon.id: Descending\",\"params\":{}}],\"metrics\":[{\"accessor\":4,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"label\":\"Cantidad Eventos \",\"params\":{}}]},\"perPage\":10,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Logged on Administrators [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-804dd400-a248-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwOSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4672\"},\"type\":\"phrase\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4672\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Admin Logons Simple [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Admin Logons\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"aggType\":\"cardinality\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Admin Logons Simple [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5bb93ed0-a249-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzkxLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4672\"},\"type\":\"phrase\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4672\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Administrator Users [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"legendOpen\":true}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"field\":\"winlog.logon.id\"},\"schema\":\"metric\",\"type\":\"cardinality\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"user.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":10},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"label\":\"user.name: Descending\",\"params\":{}}],\"metric\":{\"accessor\":1,\"aggType\":\"cardinality\",\"format\":{\"id\":\"number\"},\"label\":\"Unique count of winlog.logon.id\",\"params\":{}}},\"isDonut\":false,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"bottom\",\"type\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"Administrator Users [Windows System Security]\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-e2516c10-a249-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2OCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "User Logon Dashboard [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":10,\"markdown\":\"## **Logon Information Dashboard**\",\"openLinksInNewTab\":false},\"title\":\"User Logon Dashboard [Windows System Security]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-18348f30-a24d-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzU3LDFd" +} + +{ + "attributes": { + "columns": [ + "user.name", + "winlog.logon.type", + "source.domain", + "source.ip", + "winlog.logon.id" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4624\"},\"type\":\"phrase\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4624\",\"type\":\"phrase\"}}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "User Logons [Windows System Security]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-ce71c9a0-a25e-11e9-a422-d144027429da", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE5MSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4624\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"event.code\":\"4624\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logons Simple [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Logons\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"aggType\":\"cardinality\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Logons Simple [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-2c71e0f0-9c0d-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzY4LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4624\",\"4672\"],\"type\":\"phrases\",\"value\":\"4624, 4672\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4624\"}},{\"match_phrase\":{\"event.code\":\"4672\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logon Events in Time - Simple [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"colors\":{\"Admin Logons\":\"#E24D42\",\"Logon Events\":\"#447EBC\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"drop_partials\":false,\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"scaleMetricValues\":false,\"timeRange\":{\"from\":\"2020-05-20T07:35:27.496Z\",\"to\":\"2020-05-22T00:01:10.239Z\"},\"useNormalizedEsInterval\":true},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"filters\":[{\"input\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4624\\\" \"},\"label\":\"Logon Events\"},{\"input\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4672\\\" \"},\"label\":\"Admin Logons\"}]},\"schema\":\"group\",\"type\":\"filters\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"filter\":true,\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false},\"labels\":{},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"interpolate\":\"cardinal\",\"lineWidth\":2,\"mode\":\"normal\",\"show\":true,\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\",\"circlesRadius\":1}],\"thresholdLine\":{\"color\":\"#E7664C\",\"show\":false,\"style\":\"full\",\"value\":10,\"width\":1},\"times\":[],\"type\":\"line\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"linear\"},\"title\":\"Logon Events in Time - Simple [Windows System Security]\",\"type\":\"line\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-abd44840-9c0f-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0MCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security \"}}" + }, + "savedSearchRefName": "search_0", + "title": "Logon Sources [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"source.ip\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":15},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"maxFontSize\":72,\"minFontSize\":18,\"orientation\":\"single\",\"scale\":\"linear\",\"showLabel\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"title\":\"Logon Sources [Windows System Security]\",\"type\":\"tagcloud\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-21aadac0-9c0b-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-7e178c80-fee1-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzY0LDFd" +} + +{ + "attributes": { + "columns": [ + "user.name", + "source.domain", + "source.ip", + "winlog.logon.id", + "event.action" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4778\",\"4779\"],\"type\":\"phrases\",\"value\":\"4778, 4779\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4778\"}},{\"match_phrase\":{\"event.code\":\"4779\"}}]}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Remote Interactive Connections and Disconnections [Windows System Security]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-6f4071a0-7a78-11ea-bc9a-0baf2ca323a3", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4NCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4648\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"event.code\":\"4648\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logon with Explicit Credentials [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"user.name\",\"field\":\"user.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":200},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"subjectUserName\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"source.ip\",\"field\":\"source.ip\",\"json\":\"{\\\"missing\\\": \\\"::\\\"}\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"6\",\"params\":{\"customLabel\":\"LogonID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"perPage\":10,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Logon with Explicit Credentials [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-25f31ee0-9c23-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzY1LDFd" +} + +{ + "attributes": { + "columns": [ + "user.name", + "user.domain", + "winlog.logon.id", + "event.action", + "winlog.logon.type", + "winlog.event_data.SubjectUserName" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4625\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"event.code\":\"4625\"}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\",\"key\":\"winlog.provider_name\",\"negate\":false,\"params\":{\"query\":\"Microsoft-Windows-Security-Auditing\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"winlog.provider_name\":\"Microsoft-Windows-Security-Auditing\"}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "User Logouts [Windows System Security]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-06b6b060-7a80-11ea-bc9a-0baf2ca323a3", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4MSwxXQ==" +} + +{ + "attributes": { + "description": "User logon activity dashboard with TSVB metrics.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":28,\"i\":\"1\",\"w\":18,\"x\":0,\"y\":38},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Sesiones Usuarios Admin\",\"panelRefName\":\"panel_1\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"2\",\"w\":9,\"x\":0,\"y\":6},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_2\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":19,\"i\":\"3\",\"w\":18,\"x\":0,\"y\":19},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Usuarios Adm\",\"panelRefName\":\"panel_3\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":6,\"i\":\"4\",\"w\":12,\"x\":0,\"y\":0},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_4\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":27,\"i\":\"10\",\"w\":22,\"x\":0,\"y\":66},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Network Logon Details\",\"panelRefName\":\"panel_10\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":6,\"i\":\"08245e0c-6afe-43ea-ba5f-76c3b17301fd\",\"w\":36,\"x\":12,\"y\":0},\"panelIndex\":\"08245e0c-6afe-43ea-ba5f-76c3b17301fd\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_08245e0c-6afe-43ea-ba5f-76c3b17301fd\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"f403fdcc-6588-4573-a949-9e661783a2b8\",\"w\":9,\"x\":9,\"y\":6},\"panelIndex\":\"f403fdcc-6588-4573-a949-9e661783a2b8\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_f403fdcc-6588-4573-a949-9e661783a2b8\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"51a9affa-8e96-42bd-98e9-80531bdefc53\",\"w\":30,\"x\":18,\"y\":6},\"panelIndex\":\"51a9affa-8e96-42bd-98e9-80531bdefc53\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Events Timeline\",\"panelRefName\":\"panel_51a9affa-8e96-42bd-98e9-80531bdefc53\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":19,\"i\":\"bbdca4de-11c5-4957-a74c-73769416a562\",\"w\":12,\"x\":18,\"y\":19},\"panelIndex\":\"bbdca4de-11c5-4957-a74c-73769416a562\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Types\",\"panelRefName\":\"panel_bbdca4de-11c5-4957-a74c-73769416a562\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":19,\"i\":\"4df66ae6-e047-47c7-b1a9-b15221eb9d90\",\"w\":18,\"x\":30,\"y\":19},\"panelIndex\":\"4df66ae6-e047-47c7-b1a9-b15221eb9d90\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4df66ae6-e047-47c7-b1a9-b15221eb9d90\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":28,\"i\":\"454bb008-9720-455e-8ab9-b2f47d25aa4f\",\"w\":19,\"x\":18,\"y\":38},\"panelIndex\":\"454bb008-9720-455e-8ab9-b2f47d25aa4f\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"RDP Reconnections and Desconnections\",\"panelRefName\":\"panel_454bb008-9720-455e-8ab9-b2f47d25aa4f\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":28,\"i\":\"baec73e7-7166-4577-9483-1252bdd8773c\",\"w\":11,\"x\":37,\"y\":38},\"panelIndex\":\"baec73e7-7166-4577-9483-1252bdd8773c\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_baec73e7-7166-4577-9483-1252bdd8773c\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":27,\"i\":\"28115147-8399-4fcd-95ce-ed0a4f4239e3\",\"w\":26,\"x\":22,\"y\":66},\"panelIndex\":\"28115147-8399-4fcd-95ce-ed0a4f4239e3\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logout Details\",\"panelRefName\":\"panel_28115147-8399-4fcd-95ce-ed0a4f4239e3\"}]", + "timeRestore": false, + "title": "[System Windows Security] User Logons - Simple Metrics", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-035846a0-a249-11e9-a422-d144027429da", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-804dd400-a248-11e9-a422-d144027429da", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-5bb93ed0-a249-11e9-a422-d144027429da", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "system-e2516c10-a249-11e9-a422-d144027429da", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-18348f30-a24d-11e9-a422-d144027429da", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "system-ce71c9a0-a25e-11e9-a422-d144027429da", + "name": "10:panel_10", + "type": "search" + }, + { + "id": "system-d770b040-9b35-11ea-87e4-49f31ec44891", + "name": "08245e0c-6afe-43ea-ba5f-76c3b17301fd:panel_08245e0c-6afe-43ea-ba5f-76c3b17301fd", + "type": "visualization" + }, + { + "id": "system-2c71e0f0-9c0d-11ea-87e4-49f31ec44891", + "name": "f403fdcc-6588-4573-a949-9e661783a2b8:panel_f403fdcc-6588-4573-a949-9e661783a2b8", + "type": "visualization" + }, + { + "id": "system-abd44840-9c0f-11ea-87e4-49f31ec44891", + "name": "51a9affa-8e96-42bd-98e9-80531bdefc53:panel_51a9affa-8e96-42bd-98e9-80531bdefc53", + "type": "visualization" + }, + { + "id": "system-006d75f0-9c03-11ea-87e4-49f31ec44891", + "name": "bbdca4de-11c5-4957-a74c-73769416a562:panel_bbdca4de-11c5-4957-a74c-73769416a562", + "type": "visualization" + }, + { + "id": "system-21aadac0-9c0b-11ea-87e4-49f31ec44891", + "name": "4df66ae6-e047-47c7-b1a9-b15221eb9d90:panel_4df66ae6-e047-47c7-b1a9-b15221eb9d90", + "type": "visualization" + }, + { + "id": "system-6f4071a0-7a78-11ea-bc9a-0baf2ca323a3", + "name": "454bb008-9720-455e-8ab9-b2f47d25aa4f:panel_454bb008-9720-455e-8ab9-b2f47d25aa4f", + "type": "search" + }, + { + "id": "system-25f31ee0-9c23-11ea-87e4-49f31ec44891", + "name": "baec73e7-7166-4577-9483-1252bdd8773c:panel_baec73e7-7166-4577-9483-1252bdd8773c", + "type": "visualization" + }, + { + "id": "system-06b6b060-7a80-11ea-bc9a-0baf2ca323a3", + "name": "28115147-8399-4fcd-95ce-ed0a4f4239e3:panel_28115147-8399-4fcd-95ce-ed0a4f4239e3", + "type": "search" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzI3LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4722\"},\"type\":\"phrase\",\"value\":\"4722\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4722\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security \"}}" + }, + "title": "Users Enabled - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Enabled User\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performer LogonId\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Enabled - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-0620c3d0-bcd4-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzQ3LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Administrator Logons [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"d5bcde50-9bfc-11ea-aaa3-618beeff2d9c\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(181,49,0,1)\",\"id\":\"16018150-9bfd-11ea-aaa3-618beeff2d9c\",\"operator\":\"gte\",\"value\":0},{\"background_color\":\"rgba(181,49,0,1)\",\"id\":\"3dfd0857-b402-439c-9dd3-79069ee07878\",\"operator\":\"empty\",\"value\":null}],\"filter\":{\"language\":\"kuery\",\"query\":\"((data_stream.dataset:windows.security OR data_stream.dataset:system.security) AND event.code: \\\"4672\\\")\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Administrator Logons\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Administrator Logons [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-0622da40-9bfd-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzQ4LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Network Traffic (Bytes) [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"listeners\":{},\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"lucene\",\"query\":\"-system.network.name:l*\"},\"id\":\"da1046f0-faa0-11e6-86b1-cd7735ff7e23\",\"index_pattern\":\"*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(0,156,224,1)\",\"fill\":\"1\",\"formatter\":\"bytes\",\"id\":\"da1046f1-faa0-11e6-86b1-cd7735ff7e23\",\"label\":\"Inbound \",\"line_width\":\"0\",\"metrics\":[{\"field\":\"system.network.in.bytes\",\"id\":\"da1046f2-faa0-11e6-86b1-cd7735ff7e23\",\"type\":\"max\"},{\"field\":\"da1046f2-faa0-11e6-86b1-cd7735ff7e23\",\"id\":\"f41f9280-faa0-11e6-86b1-cd7735ff7e23\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"field\":\"f41f9280-faa0-11e6-86b1-cd7735ff7e23\",\"id\":\"a87398e0-1b93-11e7-8ada-3df93aab833e\",\"type\":\"positive_only\",\"unit\":\"\"},{\"function\":\"sum\",\"id\":\"2d533df0-2c2d-11e7-be71-3162da85303f\",\"type\":\"series_agg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"value_template\":\"{{value}}/s\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(250,40,255,1)\",\"fill\":\"1\",\"formatter\":\"bytes\",\"id\":\"fbbd5720-faa0-11e6-86b1-cd7735ff7e23\",\"label\":\"Outbound \",\"line_width\":\"0\",\"metrics\":[{\"field\":\"system.network.out.bytes\",\"id\":\"fbbd7e30-faa0-11e6-86b1-cd7735ff7e23\",\"type\":\"max\"},{\"field\":\"fbbd7e30-faa0-11e6-86b1-cd7735ff7e23\",\"id\":\"fbbd7e31-faa0-11e6-86b1-cd7735ff7e23\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"id\":\"17e597a0-faa1-11e6-86b1-cd7735ff7e23\",\"script\":\"params.rate != null && params.rate > 0 ? params.rate * -1 : null\",\"type\":\"calculation\",\"variables\":[{\"field\":\"fbbd7e31-faa0-11e6-86b1-cd7735ff7e23\",\"id\":\"1940bad0-faa1-11e6-86b1-cd7735ff7e23\",\"name\":\"rate\"}]},{\"function\":\"sum\",\"id\":\"533da9b0-2c2d-11e7-be71-3162da85303f\",\"type\":\"series_agg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"value_template\":\"{{value}}/s\"}],\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"Mericbeat: Network Traffic (Bytes)\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-089b85d0-1b16-11e7-b09e-037021c4f8df", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzQ5LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4725\"},\"type\":\"phrase\",\"value\":\"4725\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4725\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Disabled - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Disabled Users\",\"field\":\"user.name\"},\"schema\":\"metric\",\"type\":\"cardinality\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Disabled - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-0cb2d940-bcde-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzUwLDFd" +} + +{ + "attributes": { + "columns": [ + "user.name", + "user.id", + "group.id", + "system.auth.useradd.home", + "system.auth.useradd.shell" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"system.auth.useradd:*\"}}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "useradd logs [Logs System]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-8030c1b0-fa77-11e6-ae9b-81e5311e8cab", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4NywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "New users [Logs System]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Host\",\"field\":\"host.hostname\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"User\",\"field\":\"user.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"UID\",\"field\":\"user.id\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"GID\",\"field\":\"group.id\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"6\",\"params\":{\"customLabel\":\"Home\",\"field\":\"system.auth.useradd.home\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"7\",\"params\":{\"customLabel\":\"Shell\",\"field\":\"system.auth.useradd.shell\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.auth\\\"\"},\"perPage\":10,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"New users\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-f398d2f0-fa77-11e6-ae9b-81e5311e8cab", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-8030c1b0-fa77-11e6-ae9b-81e5311e8cab", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE3MSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "New users over time [Logs System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"user.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"bottom\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[]},\"title\":\"New users over time\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5dd15c00-fa78-11e6-ae9b-81e5311e8cab", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-8030c1b0-fa77-11e6-ae9b-81e5311e8cab", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzk2LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "New users by shell [Logs System]", + "uiStateJSON": "{\"vis\":{\"colors\":{\"/bin/bash\":\"#E24D42\",\"/bin/false\":\"#508642\",\"/sbin/nologin\":\"#7EB26D\"},\"legendOpen\":true}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"system.auth.useradd.shell\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"user.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTooltip\":true,\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.auth\\\"\"},\"isDonut\":false,\"legendPosition\":\"right\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"New users by shell\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-e121b140-fa78-11e6-a1df-a78bd7504d38", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-8030c1b0-fa77-11e6-ae9b-81e5311e8cab", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2NSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "New users by home directory [Logs System]", + "uiStateJSON": "{\"vis\":{\"colors\":{\"/bin/bash\":\"#E24D42\",\"/bin/false\":\"#508642\",\"/nonexistent\":\"#629E51\",\"/sbin/nologin\":\"#7EB26D\"},\"legendOpen\":true}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"system.auth.useradd.home\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"user.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":false,\"legendPosition\":\"right\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"New users by home directory\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-d56ee420-fa79-11e6-a1df-a78bd7504d38", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-8030c1b0-fa77-11e6-ae9b-81e5311e8cab", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1OSwxXQ==" +} + +{ + "attributes": { + "columns": [ + "group.name", + "group.id" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"system.auth.groupadd:*\"}}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "groupadd logs [Logs System]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-eb0039f0-fa7f-11e6-a1df-a78bd7504d38", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE5MiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "New groups [Logs System]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"group.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"group.id\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"perPage\":10,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"New groups\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-12667040-fa80-11e6-a1df-a78bd7504d38", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-eb0039f0-fa7f-11e6-a1df-a78bd7504d38", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzU0LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "New groups over time [Logs System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"group.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"bottom\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[]},\"title\":\"New groups over time\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-346bb290-fa80-11e6-a1df-a78bd7504d38", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-eb0039f0-fa7f-11e6-a1df-a78bd7504d38", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzc0LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Dashboards [Logs System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":12,\"markdown\":\"[Syslog](#/dashboard/system-Logs-syslog-dashboard) | [Sudo commands](#/dashboard/system-277876d0-fa2c-11e6-bbd3-29c986c96e5a) | [SSH logins](#/dashboard/system-5517a150-f9ce-11e6-8115-a7c18106d86a) | [New users and groups](#/dashboard/system-0d3f2380-fa78-11e6-ae9b-81e5311e8cab)\"},\"title\":\"Dashboards [Logs System]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-327417e0-8462-11e7-bab8-bd2f0fb42c54", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzcxLDFd" +} + +{ + "attributes": { + "description": "New users and groups dashboard for the System integration in Logs", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":4},\"panelIndex\":\"1\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":4},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":16},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":16},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":28},\"panelIndex\":\"5\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":28},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":4,\"i\":\"7\",\"w\":48,\"x\":0,\"y\":0},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"}]", + "timeRestore": false, + "title": "[Logs System] New users and groups", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-0d3f2380-fa78-11e6-ae9b-81e5311e8cab", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-f398d2f0-fa77-11e6-ae9b-81e5311e8cab", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-5dd15c00-fa78-11e6-ae9b-81e5311e8cab", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "system-e121b140-fa78-11e6-a1df-a78bd7504d38", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-d56ee420-fa79-11e6-a1df-a78bd7504d38", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "system-12667040-fa80-11e6-a1df-a78bd7504d38", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "system-346bb290-fa80-11e6-a1df-a78bd7504d38", + "name": "6:panel_6", + "type": "visualization" + }, + { + "id": "system-327417e0-8462-11e7-bab8-bd2f0fb42c54", + "name": "7:panel_7", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzI4LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4720\"},\"type\":\"phrase\",\"value\":\"4720\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4720\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Created - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Users Created\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Created - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-102efd20-bcdd-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzUyLDFd" +} + +{ + "attributes": { + "columns": [ + "event.action", + "winlog.event_data.TargetUserName", + "user.domain", + "user.name", + "winlog.event_data.SubjectDomainName", + "winlog.logon.id", + "related.user" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4720\",\"4722\",\"4723\",\"4724\",\"4725\",\"4726\",\"4738\",\"4740\",\"4767\",\"4781\",\"4798\"],\"type\":\"phrases\",\"value\":\"4720, 4722, 4723, 4724, 4725, 4726, 4738, 4740, 4767, 4781, 4798\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4720\"}},{\"match_phrase\":{\"event.code\":\"4722\"}},{\"match_phrase\":{\"event.code\":\"4723\"}},{\"match_phrase\":{\"event.code\":\"4724\"}},{\"match_phrase\":{\"event.code\":\"4725\"}},{\"match_phrase\":{\"event.code\":\"4726\"}},{\"match_phrase\":{\"event.code\":\"4738\"}},{\"match_phrase\":{\"event.code\":\"4740\"}},{\"match_phrase\":{\"event.code\":\"4767\"}},{\"match_phrase\":{\"event.code\":\"4781\"}},{\"match_phrase\":{\"event.code\":\"4798\"}}]}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "User management Details - Search [Windows System Security]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-324686c0-fefb-11e9-8405-516218e3d268", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4MiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Target Users [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":10},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"maxFontSize\":72,\"minFontSize\":18,\"orientation\":\"single\",\"scale\":\"linear\",\"showLabel\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"title\":\"Target Users [Windows System Security]\",\"type\":\"tagcloud\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-117f5a30-9b71-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-324686c0-fefb-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzUzLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"winlog.provider_name\",\"negate\":false,\"params\":{\"query\":\"Microsoft-Windows-Security-Auditing\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"winlog.provider_name\":\"Microsoft-Windows-Security-Auditing\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logon Successful - Logon Failed Timeline [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"colors\":{\"Login Failed\":\"#F9934E\",\"Login OK\":\"#9AC48A\",\"Logon Failed\":\"#EF843C\",\"Logon Successful\":\"#9AC48A\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"drop_partials\":false,\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"scaleMetricValues\":false,\"timeRange\":{\"from\":\"2020-05-17T09:37:55.995Z\",\"to\":\"2020-05-22T03:09:27.260Z\"},\"useNormalizedEsInterval\":true},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"filters\":[{\"input\":{\"language\":\"lucene\",\"query\":\"event.code: 4624\"},\"label\":\"Logon Successful\"},{\"input\":{\"language\":\"lucene\",\"query\":\"event.code: 4625\"},\"label\":\"Logon Failed\"}]},\"schema\":\"group\",\"type\":\"filters\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"dimensions\":{\"series\":[{\"accessor\":1,\"aggType\":\"filters\",\"format\":{},\"params\":{}}],\"x\":{\"accessor\":0,\"aggType\":\"date_histogram\",\"format\":{\"id\":\"date\",\"params\":{\"pattern\":\"HH:mm\"}},\"params\":{\"bounds\":{\"max\":\"2019-07-16T14:30:11.515Z\",\"min\":\"2019-07-16T12:30:11.514Z\"},\"date\":true,\"format\":\"HH:mm\",\"interval\":\"PT1M\"}},\"y\":[{\"accessor\":2,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"grid\":{\"categoryLines\":false},\"labels\":{\"show\":false},\"legendPosition\":\"bottom\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"mode\":\"stacked\",\"show\":\"true\",\"showCircles\":true,\"type\":\"histogram\",\"valueAxis\":\"ValueAxis-1\",\"circlesRadius\":1}],\"thresholdLine\":{\"color\":\"#E7664C\",\"show\":false,\"style\":\"full\",\"value\":10,\"width\":1},\"times\":[],\"type\":\"histogram\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"title\":\"Logon Successful - Logon Failed Timeline [Windows System Security]\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-162d7ab0-a7d6-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzU1LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"winlog.provider_name\",\"negate\":false,\"params\":{\"query\":\"Microsoft-Windows-Security-Auditing\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"winlog.provider_name\":\"Microsoft-Windows-Security-Auditing\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logon Successful vs Failed [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"colors\":{\"Failed Logins\":\"#EF843C\",\"Failed Logons\":\"#EA6460\",\"Successful Login\":\"#B7DBAB\",\"Successful Logon\":\"#B7DBAB\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"filters\":[{\"input\":{\"language\":\"lucene\",\"query\":\"event.code: 4624\"},\"label\":\"Successful Logon\"},{\"input\":{\"language\":\"lucene\",\"query\":\"event.code: 4625\"},\"label\":\"Failed Logons\"}]},\"schema\":\"segment\",\"type\":\"filters\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"filters\",\"format\":{},\"params\":{}}],\"metric\":{\"accessor\":1,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}},\"isDonut\":false,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"bottom\",\"type\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"Logon Successful vs Failed [Windows System Security]\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-175a5760-a7d5-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzU2LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Swap usage [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.memory\\\"\"},\"gauge_color_rules\":[{\"gauge\":\"rgba(104,188,0,1)\",\"id\":\"d17c1e90-4d59-11e7-aee5-fdc812cc3bec\",\"operator\":\"gte\",\"value\":0},{\"gauge\":\"rgba(251,158,0,1)\",\"id\":\"fc1d3490-4d59-11e7-aee5-fdc812cc3bec\",\"operator\":\"gte\",\"value\":0.7},{\"gauge\":\"rgba(211,49,21,1)\",\"id\":\"0e204240-4d5a-11e7-aee5-fdc812cc3bec\",\"operator\":\"gte\",\"value\":0.85},{\"gauge\":\"rgba(104,188,0,1)\",\"id\":\"155d9fe4-2c72-4bcc-b9a3-5eb3dca790ad\",\"operator\":\"empty\",\"value\":null}],\"gauge_inner_width\":10,\"gauge_max\":\"\",\"gauge_style\":\"half\",\"gauge_width\":10,\"id\":\"cee2fd20-4d59-11e7-aee5-fdc812cc3bec\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"cee2fd21-4d59-11e7-aee5-fdc812cc3bec\",\"label\":\"Swap usage\",\"line_width\":1,\"metrics\":[{\"field\":\"system.memory.swap.used.pct\",\"id\":\"cee2fd22-4d59-11e7-aee5-fdc812cc3bec\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"gauge\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Swap usage [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-19e123b0-4d5a-11e7-aee5-fdc812cc3bec", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzU4LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Outbound Traffic [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"background_color_rules\":[{\"id\":\"0e346760-1b92-11e7-bec4-a5e9ec5cab8b\"}],\"filter\":{\"language\":\"lucene\",\"query\":\"-system.network.name:l*\"},\"id\":\"0c761590-1b92-11e7-bec4-a5e9ec5cab8b\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"bytes\",\"id\":\"0c761591-1b92-11e7-bec4-a5e9ec5cab8b\",\"label\":\"Outbound Traffic\",\"line_width\":1,\"metrics\":[{\"field\":\"system.network.out.bytes\",\"id\":\"0c761592-1b92-11e7-bec4-a5e9ec5cab8b\",\"type\":\"max\"},{\"field\":\"0c761592-1b92-11e7-bec4-a5e9ec5cab8b\",\"id\":\"1d659060-1b92-11e7-bec4-a5e9ec5cab8b\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"field\":\"1d659060-1b92-11e7-bec4-a5e9ec5cab8b\",\"id\":\"f2074f70-1b92-11e7-a416-41f5ccdba2e6\",\"type\":\"positive_only\",\"unit\":\"\"},{\"function\":\"sum\",\"id\":\"a1737470-2c55-11e7-a0ad-277ce466684d\",\"type\":\"series_agg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"value_template\":\"{{value}}/s\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"bytes\",\"id\":\"37f70440-1b92-11e7-bec4-a5e9ec5cab8b\",\"label\":\"Total Transferred\",\"line_width\":1,\"metrics\":[{\"field\":\"system.network.out.bytes\",\"id\":\"37f72b50-1b92-11e7-bec4-a5e9ec5cab8b\",\"type\":\"max\"},{\"field\":\"37f72b50-1b92-11e7-bec4-a5e9ec5cab8b\",\"id\":\"37f72b51-1b92-11e7-bec4-a5e9ec5cab8b\",\"type\":\"derivative\",\"unit\":\"\"},{\"field\":\"37f72b51-1b92-11e7-bec4-a5e9ec5cab8b\",\"id\":\"f9da2dd0-1b92-11e7-a416-41f5ccdba2e6\",\"type\":\"positive_only\",\"unit\":\"\"},{\"field\":\"f9da2dd0-1b92-11e7-a416-41f5ccdba2e6\",\"function\":\"overall_sum\",\"id\":\"3e63c2f0-1b92-11e7-bec4-a5e9ec5cab8b\",\"sigma\":\"\",\"type\":\"series_agg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"value_template\":\"{{value}}\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Outbound Traffic [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-1aae9140-1b93-11e7-8ada-3df93aab833e", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzU5LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Unlocks - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(116,167,167,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"69e8f84e-7ff9-4e7b-a5d7-d6f9105ec54e\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4767\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Unlocks\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Unlocks - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-1b6725f0-ff1d-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzYxLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Renamed TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(110,139,162,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"4a8d8644-1c32-4c73-9449-565a57ee277b\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4781\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Renamed\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Renamed TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-1f271bc0-231a-11ea-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzYyLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4625\"},\"type\":\"phrase\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4625\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\",\"key\":\"winlog.provider_name\",\"negate\":false,\"params\":{\"query\":\"Microsoft-Windows-Security-Auditing\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"winlog.provider_name\":\"Microsoft-Windows-Security-Auditing\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logon Failed Source IP [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"source.ip\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"bucket\":{\"accessor\":0,\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"ip\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"type\":\"vis_dimension\"},\"maxFontSize\":38,\"metric\":{\"accessor\":1,\"format\":{\"id\":\"string\",\"params\":{}},\"type\":\"vis_dimension\"},\"minFontSize\":10,\"orientation\":\"single\",\"scale\":\"linear\",\"showLabel\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"title\":\"Logon Failed Source IP [Windows System Security]\",\"type\":\"tagcloud\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-2084e300-a884-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzYzLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Load Gauge [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"background_color_rules\":[{\"id\":\"feefabd0-1b90-11e7-bec4-a5e9ec5cab8b\"}],\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.load\\\" \"},\"gauge_color_rules\":[{\"id\":\"ffd94880-1b90-11e7-bec4-a5e9ec5cab8b\"}],\"gauge_inner_width\":10,\"gauge_style\":\"half\",\"gauge_width\":10,\"id\":\"fdcc6180-1b90-11e7-bec4-a5e9ec5cab8b\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"fdcc6181-1b90-11e7-bec4-a5e9ec5cab8b\",\"label\":\"5m Load\",\"line_width\":1,\"metrics\":[{\"field\":\"system.load.5\",\"id\":\"fdcc6182-1b90-11e7-bec4-a5e9ec5cab8b\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"gauge\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Load Gauge [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-26732e20-1b91-11e7-bec4-a5e9ec5cab8b", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzY2LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "User Management Actions [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":15},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":false,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"right\",\"type\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"User Management Actions [Windows System Security]\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-26877510-9b72-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-324686c0-fefb-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzY3LDFd" +} + +{ + "attributes": { + "columns": [ + "user.name", + "system.auth.sudo.user", + "system.auth.sudo.pwd", + "system.auth.sudo.command" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"system.auth.sudo:*\"}}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Sudo commands [Logs System]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-b6f321e0-fa25-11e6-bbd3-29c986c96e5a", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE5MCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "Sudo commands by user [Logs System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"user.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[]},\"title\":\"Sudo commands by user\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5c7af030-fa2a-11e6-bbd3-29c986c96e5a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-b6f321e0-fa25-11e6-bbd3-29c986c96e5a", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzkyLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"system.auth.sudo.error:*\"}}" + }, + "title": "Sudo errors [Logs System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"system.auth.sudo.error\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[]},\"title\":\"Sudo errors\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-51164310-fa2b-11e6-bbd3-29c986c96e5a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzg1LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "Top sudo commands [Logs System]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"system.auth.sudo.command\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"user.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.auth\\\"\"},\"perPage\":10,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Top sudo commands\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-dc589770-fa2b-11e6-bbd3-29c986c96e5a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-b6f321e0-fa25-11e6-bbd3-29c986c96e5a", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2MywxXQ==" +} + +{ + "attributes": { + "description": "Sudo commands dashboard from the Logs System integration", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"1\",\"w\":48,\"x\":0,\"y\":20},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"2\",\"w\":48,\"x\":0,\"y\":36},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"3\",\"w\":48,\"x\":0,\"y\":4},\"panelIndex\":\"3\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":4,\"i\":\"4\",\"w\":48,\"x\":0,\"y\":0},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"}]", + "timeRestore": false, + "title": "[Logs System] Sudo commands", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-277876d0-fa2c-11e6-bbd3-29c986c96e5a", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-5c7af030-fa2a-11e6-bbd3-29c986c96e5a", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-51164310-fa2b-11e6-bbd3-29c986c96e5a", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "system-dc589770-fa2b-11e6-bbd3-29c986c96e5a", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-327417e0-8462-11e7-bab8-bd2f0fb42c54", + "name": "4:panel_4", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzI5LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "User Management Events - Description [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":10,\"markdown\":\"# **User Management Events**\\n\\n#### This dashboard shows information about User Management Events collected by winlogbeat\\n\",\"openLinksInNewTab\":false},\"title\":\"User Management Events - Description [Windows System Security]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-2dc6b820-b9e8-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzY5LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Processes By Memory [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"bar_color_rules\":[{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"efb9b660-1b18-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0},{\"bar_color\":\"rgba(254,146,0,1)\",\"id\":\"17fcb820-1b19-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0.7},{\"bar_color\":\"rgba(211,49,21,1)\",\"id\":\"1dd61070-1b19-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0.85},{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"f1b5d9ea-6b7b-4aaa-9172-f9ad77b72d8e\",\"operator\":\"empty\",\"value\":null}],\"drilldown_url\":\"\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.process\\\" \"},\"id\":\"edfceb30-1b18-11e7-b09e-037021c4f8df\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"edfceb31-1b18-11e7-b09e-037021c4f8df\",\"line_width\":1,\"metrics\":[{\"field\":\"system.process.memory.rss.pct\",\"id\":\"edfceb32-1b18-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"process.name\",\"terms_order_by\":\"edfceb32-1b18-11e7-b09e-037021c4f8df\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"top_n\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Processes By Memory [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-2e224660-1b19-11e7-b09e-037021c4f8df", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzcwLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"system.auth.ssh.event:Failed OR system.auth.ssh.event:Invalid\"}}" + }, + "title": "SSH users of failed login attempts [Logs System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"user.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":50},\"schema\":\"segment\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"maxFontSize\":72,\"minFontSize\":18,\"orientation\":\"single\",\"scale\":\"linear\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"title\":\"SSH users of failed login attempts\",\"type\":\"tagcloud\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-341ffe70-f9ce-11e6-8115-a7c18106d86a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzczLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Disk Usage [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"bar_color_rules\":[{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"bf525310-1b95-11e7-8ada-3df93aab833e\",\"operator\":\"gte\",\"value\":0},{\"bar_color\":\"rgba(254,146,0,1)\",\"id\":\"125fc4c0-1b96-11e7-8ada-3df93aab833e\",\"operator\":\"gte\",\"value\":0.7},{\"bar_color\":\"rgba(211,49,21,1)\",\"id\":\"1a5c7240-1b96-11e7-8ada-3df93aab833e\",\"operator\":\"gte\",\"value\":0.85},{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"d0760fcb-e52e-4440-bcab-4817cfee41d3\",\"operator\":\"empty\",\"value\":null}],\"drilldown_url\":\"\",\"filter\":{\"language\":\"lucene\",\"query\":\"-system.filesystem.mount_point:\\\\/run* AND -system.filesystem.mount_point:\\\\/sys* AND -system.filesystem.mount_point:\\\\/dev* AND -system.filesystem.mount_point:\\\\/proc* AND -system.filesystem.mount_point:\\\\/var* AND -system.filesystem.mount_point:\\\\/boot\"},\"id\":\"9f7e48a0-1b95-11e7-8ada-3df93aab833e\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"9f7e48a1-1b95-11e7-8ada-3df93aab833e\",\"line_width\":1,\"metrics\":[{\"agg_with\":\"avg\",\"field\":\"system.filesystem.used.pct\",\"id\":\"9f7e48a2-1b95-11e7-8ada-3df93aab833e\",\"order\":\"desc\",\"order_by\":\"@timestamp\",\"size\":1,\"type\":\"top_hit\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.filesystem.mount_point\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"top_n\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Disk Usage [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-34f97ee0-1b96-11e7-8ada-3df93aab833e", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzc1LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"system.auth.ssh.event:Failed OR system.auth.ssh.event:Invalid\"}}" + }, + "title": "SSH failed login attempts source locations [Logs System]", + "uiStateJSON": "{\"mapCenter\":[17.602139123350838,69.697265625],\"mapZoom\":2}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"autoPrecision\":true,\"field\":\"source.geo.location\",\"precision\":2},\"schema\":\"segment\",\"type\":\"geohash_grid\"}],\"listeners\":{},\"params\":{\"addTooltip\":true,\"heatBlur\":15,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatNormalizeData\":true,\"heatRadius\":25,\"isDesaturated\":true,\"legendPosition\":\"bottomright\",\"mapCenter\":[15,5],\"mapType\":\"Shaded Circle Markers\",\"mapZoom\":2,\"wms\":{\"enabled\":false,\"options\":{\"attribution\":\"Maps provided by USGS\",\"format\":\"image/png\",\"layers\":\"0\",\"styles\":\"\",\"transparent\":true,\"version\":\"1.3.0\"},\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\"}},\"title\":\"SSH failed login attempts source locations\",\"type\":\"tile_map\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-3cec3eb0-f9d3-11e6-8a3e-2b904044ea1d", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzc2LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Tip [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":12,\"markdown\":\"**TIP:** To select another host, go to the [System Overview](#/dashboard/system-Metrics-system-overview) dashboard and double-click a host name.\"},\"title\":\"Tip [Metrics System]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-3d65d450-a9c3-11e7-af20-67db8aecb295", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzc3LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Groups Changed TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(200,201,197,1)\",\"id\":\"bfcaced0-f419-11e9-928e-8f5fd2b6c66e\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(221,186,64,1)\",\"id\":\"a7d935e0-f497-11e9-928e-8f5fd2b6c66e\",\"operator\":\"gt\",\"value\":0},{\"background_color\":\"rgba(200,201,197,1)\",\"id\":\"c502e012-3e4e-45e9-abc4-625973d246d5\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code:4735 OR event.code:4737 OR event.code:\\\"4755\\\" OR event.code:\\\"4764\\\" OR event.code:\\\"4750\\\" OR event.code:\\\"4760\\\" OR event.code:\\\"4745\\\" OR event.code:\\\"4784\\\" OR event.code:\\\"4791\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"60d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Groups Changed\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Groups Changed TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-400b63e0-f49a-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzc4LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4625\"},\"type\":\"phrase\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4625\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\",\"key\":\"winlog.provider_name\",\"negate\":false,\"params\":{\"query\":\"Microsoft-Windows-Security-Auditing\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"winlog.provider_name\":\"Microsoft-Windows-Security-Auditing\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logon Failed Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Time Bucket\",\"drop_partials\":false,\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"h\",\"min_doc_count\":1,\"scaleMetricValues\":false,\"timeRange\":{\"from\":\"2020-05-17T09:37:55.995Z\",\"to\":\"2020-05-22T03:09:27.260Z\"},\"useNormalizedEsInterval\":true},\"schema\":\"bucket\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"user.name\",\"field\":\"user.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":1000},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"source workstation\",\"field\":\"source.domain\",\"json\":\"{\\\"missing\\\": \\\"N/A\\\"}\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"5\",\"params\":{\"customLabel\":\"source.ip\",\"field\":\"source.ip\",\"json\":\"{\\\"missing\\\": \\\"::\\\"}\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"6\",\"params\":{\"customLabel\":\"event.action\",\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"7\",\"params\":{\"customLabel\":\"winlog.logon.type\",\"field\":\"winlog.logon.type\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"8\",\"params\":{\"customLabel\":\"winlog.event_data.SubjectUserName\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"date_histogram\",\"format\":{\"id\":\"date\",\"params\":{\"pattern\":\"YYYY-MM-DD HH:mm\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":3,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"ip\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":4,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":5,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":15,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Logon Failed Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-421f0610-af98-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzc5LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4740\"},\"type\":\"phrase\",\"value\":\"4740\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4740\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Locked Out - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Locked User\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performer LogonId\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Locked Out - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-4ac8f5f0-bcfe-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzgwLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4625\"],\"type\":\"phrases\",\"value\":\"4625\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4625\"}}]}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\",\"key\":\"winlog.provider_name\",\"negate\":false,\"params\":{\"query\":\"Microsoft-Windows-Security-Auditing\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"winlog.provider_name\":\"Microsoft-Windows-Security-Auditing\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Failed Logon HeatMap [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 4\":\"rgb(255,255,204)\",\"12 - 16\":\"rgb(252,91,46)\",\"16 - 20\":\"rgb(212,16,32)\",\"4 - 8\":\"rgb(254,225,135)\",\"8 - 12\":\"rgb(254,171,73)\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"user.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":15},\"schema\":\"segment\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"drop_partials\":true,\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"h\",\"min_doc_count\":1,\"scaleMetricValues\":false,\"timeRange\":{\"from\":\"2020-05-17T09:37:55.995Z\",\"to\":\"2020-05-22T03:09:27.260Z\"},\"useNormalizedEsInterval\":true},\"schema\":\"group\",\"type\":\"date_histogram\"}],\"params\":{\"addLegend\":true,\"addTooltip\":false,\"colorSchema\":\"Yellow to Red\",\"colorsNumber\":5,\"colorsRange\":[],\"dimensions\":{\"series\":[{\"accessor\":1,\"aggType\":\"date_histogram\",\"format\":{\"id\":\"date\",\"params\":{\"pattern\":\"YYYY-MM-DD HH:mm\"}},\"label\":\"@timestamp per hour\",\"params\":{}}],\"x\":{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"label\":\"user.name: Descending\",\"params\":{}},\"y\":[{\"accessor\":2,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"label\":\"Count\",\"params\":{}}]},\"enableHover\":true,\"invertColors\":false,\"legendPosition\":\"bottom\",\"percentageMode\":false,\"setColorRange\":false,\"times\":[],\"type\":\"heatmap\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"color\":\"black\",\"overwriteColor\":false,\"rotate\":0,\"show\":true},\"scale\":{\"defaultYExtents\":false,\"type\":\"linear\"},\"show\":false,\"type\":\"value\"}]},\"title\":\"Failed Logon HeatMap [Windows System Security]\",\"type\":\"heatmap\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-4b683ac0-a7d7-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzgxLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4625\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"event.code\":\"4625\"}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\",\"key\":\"winlog.provider_name\",\"negate\":false,\"params\":{\"query\":\"Microsoft-Windows-Security-Auditing\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"winlog.provider_name\":\"Microsoft-Windows-Security-Auditing\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": " Failed Logons [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Failed Logons\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\" Failed Logons [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-4bedf650-9ffd-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzgyLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "System Load [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.load\\\"\"},\"id\":\"f6264ad0-1b14-11e7-b09e-037021c4f8df\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(115,216,255,1)\",\"fill\":\"0\",\"formatter\":\"number\",\"id\":\"f62671e0-1b14-11e7-b09e-037021c4f8df\",\"label\":\"1m\",\"line_width\":\"3\",\"metrics\":[{\"field\":\"system.load.1\",\"id\":\"f62671e1-1b14-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(0,156,224,1)\",\"fill\":\"0\",\"formatter\":\"number\",\"id\":\"1c324850-1b15-11e7-b09e-037021c4f8df\",\"label\":\"5m\",\"line_width\":\"3\",\"metrics\":[{\"field\":\"system.load.5\",\"id\":\"1c324851-1b15-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(0,98,177,1)\",\"fill\":\"0\",\"formatter\":\"number\",\"id\":\"3287e740-1b15-11e7-b09e-037021c4f8df\",\"label\":\"15m\",\"line_width\":\"3\",\"metrics\":[{\"field\":\"system.load.15\",\"id\":\"32880e50-1b15-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"System Load [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-4d546850-1b15-11e7-b09e-037021c4f8df", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzgzLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Disk IO (Bytes) [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.diskio\\\"\"},\"id\":\"d3c67db0-1b1a-11e7-b09e-037021c4f8df\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(22,165,165,1)\",\"fill\":\"1\",\"formatter\":\"bytes\",\"id\":\"d3c67db1-1b1a-11e7-b09e-037021c4f8df\",\"label\":\"reads\",\"line_width\":1,\"metrics\":[{\"field\":\"system.diskio.read.bytes\",\"id\":\"d3c67db2-1b1a-11e7-b09e-037021c4f8df\",\"type\":\"max\"},{\"field\":\"d3c67db2-1b1a-11e7-b09e-037021c4f8df\",\"id\":\"f55b9910-1b1a-11e7-b09e-037021c4f8df\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"field\":\"f55b9910-1b1a-11e7-b09e-037021c4f8df\",\"id\":\"dcbbb100-1b93-11e7-8ada-3df93aab833e\",\"type\":\"positive_only\",\"unit\":\"\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\",\"value_template\":\"{{value}}/s\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(251,158,0,1)\",\"fill\":\"1\",\"formatter\":\"bytes\",\"id\":\"144124d0-1b1b-11e7-b09e-037021c4f8df\",\"label\":\"writes\",\"line_width\":1,\"metrics\":[{\"field\":\"system.diskio.write.bytes\",\"id\":\"144124d1-1b1b-11e7-b09e-037021c4f8df\",\"type\":\"max\"},{\"field\":\"144124d1-1b1b-11e7-b09e-037021c4f8df\",\"id\":\"144124d2-1b1b-11e7-b09e-037021c4f8df\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"id\":\"144124d4-1b1b-11e7-b09e-037021c4f8df\",\"script\":\"params.rate > 0 ? params.rate * -1 : 0\",\"type\":\"calculation\",\"variables\":[{\"field\":\"144124d2-1b1b-11e7-b09e-037021c4f8df\",\"id\":\"144124d3-1b1b-11e7-b09e-037021c4f8df\",\"name\":\"rate\"}]}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\",\"value_template\":\"{{value}}/s\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"Disk IO (Bytes) [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-4e4bb1e0-1b1b-11e7-b09e-037021c4f8df", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzg0LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Inbound Traffic [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"background_color_rules\":[{\"id\":\"0e346760-1b92-11e7-bec4-a5e9ec5cab8b\"}],\"filter\":{\"language\":\"lucene\",\"query\":\"-system.network.name:l*\"},\"id\":\"0c761590-1b92-11e7-bec4-a5e9ec5cab8b\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"bytes\",\"id\":\"0c761591-1b92-11e7-bec4-a5e9ec5cab8b\",\"label\":\"Inbound Traffic\",\"line_width\":1,\"metrics\":[{\"field\":\"system.network.in.bytes\",\"id\":\"0c761592-1b92-11e7-bec4-a5e9ec5cab8b\",\"type\":\"max\"},{\"field\":\"0c761592-1b92-11e7-bec4-a5e9ec5cab8b\",\"id\":\"1d659060-1b92-11e7-bec4-a5e9ec5cab8b\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"field\":\"1d659060-1b92-11e7-bec4-a5e9ec5cab8b\",\"id\":\"f2074f70-1b92-11e7-a416-41f5ccdba2e6\",\"type\":\"positive_only\",\"unit\":\"\"},{\"function\":\"sum\",\"id\":\"c40e18f0-2c55-11e7-a0ad-277ce466684d\",\"type\":\"series_agg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"value_template\":\"{{value}}/s\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"bytes\",\"id\":\"37f70440-1b92-11e7-bec4-a5e9ec5cab8b\",\"label\":\"Total Transferred\",\"line_width\":1,\"metrics\":[{\"field\":\"system.network.in.bytes\",\"id\":\"37f72b50-1b92-11e7-bec4-a5e9ec5cab8b\",\"type\":\"max\"},{\"field\":\"37f72b50-1b92-11e7-bec4-a5e9ec5cab8b\",\"id\":\"37f72b51-1b92-11e7-bec4-a5e9ec5cab8b\",\"type\":\"derivative\",\"unit\":\"\"},{\"field\":\"37f72b51-1b92-11e7-bec4-a5e9ec5cab8b\",\"id\":\"f9da2dd0-1b92-11e7-a416-41f5ccdba2e6\",\"type\":\"positive_only\",\"unit\":\"\"},{\"field\":\"f9da2dd0-1b92-11e7-a416-41f5ccdba2e6\",\"function\":\"overall_sum\",\"id\":\"3e63c2f0-1b92-11e7-bec4-a5e9ec5cab8b\",\"sigma\":\"\",\"type\":\"series_agg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"value_template\":\"{{value}}\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Inbound Traffic [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-522ee670-1b92-11e7-bec4-a5e9ec5cab8b", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzg2LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Groups Enumeration - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(128,128,128,1)\",\"color\":\"rgba(179,179,179,1)\",\"id\":\"bfcaced0-f419-11e9-928e-8f5fd2b6c66e\",\"operator\":\"gt\",\"value\":0},{\"background_color\":\"rgba(179,179,179,1)\",\"id\":\"8d3f3ed0-9b51-11ea-99a1-e5b989979a59\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(179,179,179,1)\",\"id\":\"ecd6f917-1c19-46f7-bce0-91b1479fe130\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code:4799\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Group Membership Enumeration\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Groups Enumeration - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-546febc0-f49b-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzg3LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"system.auth.ssh.event:Accepted\"}}" + }, + "title": "Successful SSH logins [Logs System]", + "uiStateJSON": "{\"vis\":{\"colors\":{\"Accepted\":\"#3F6833\",\"Failed\":\"#F9934E\",\"Invalid\":\"#447EBC\",\"password\":\"#BF1B00\",\"publickey\":\"#629E51\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"system.auth.ssh.method\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[]},\"title\":\"Successful SSH logins\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-d16bb400-f9cc-11e6-8115-a7c18106d86a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1NSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "SSH login attempts [Logs System]", + "uiStateJSON": "{\"vis\":{\"colors\":{\"Accepted\":\"#3F6833\",\"Failed\":\"#F9934E\",\"Invalid\":\"#447EBC\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"system.auth.ssh.event\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"times\":[]},\"title\":\"SSH login attempts\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-78b74f30-f9cd-11e6-8115-a7c18106d86a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwNSwxXQ==" +} + +{ + "attributes": { + "columns": [ + "system.auth.ssh.event", + "system.auth.ssh.method", + "user.name", + "source.ip", + "source.geo.country_iso_code" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:system.auth AND system.auth.ssh.event:*\"}}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "SSH login attempts [Logs System]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-62439dc0-f9c9-11e6-a747-6121780e0414", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4MywxXQ==" +} + +{ + "attributes": { + "description": "SSH dashboard for the System integration in Logs", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"1\",\"w\":48,\"x\":0,\"y\":16},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":12,\"i\":\"2\",\"w\":48,\"x\":0,\"y\":4},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":28},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":28},\"panelIndex\":\"4\",\"embeddableConfig\":{\"mapBounds\":{\"bottom_right\":{\"lat\":10.31491928581316,\"lon\":74.53125},\"top_left\":{\"lat\":60.50052541051131,\"lon\":-27.94921875}},\"mapCenter\":[39.774769485295465,23.203125],\"mapCollar\":{\"bottom_right\":{\"lat\":-14.777884999999998,\"lon\":125.771485},\"top_left\":{\"lat\":85.593335,\"lon\":-79.189455},\"zoom\":3},\"mapZoom\":3,\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":12,\"i\":\"5\",\"w\":48,\"x\":0,\"y\":44},\"panelIndex\":\"5\",\"embeddableConfig\":{\"columns\":[\"system.auth.ssh.event\",\"system.auth.ssh.method\",\"user.name\",\"source.ip\",\"source.geo.country_iso_code\"],\"sort\":[\"@timestamp\",\"desc\"],\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":4,\"i\":\"6\",\"w\":48,\"x\":0,\"y\":0},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"}]", + "timeRestore": false, + "title": "[Logs System] SSH login attempts", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5517a150-f9ce-11e6-8115-a7c18106d86a", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-d16bb400-f9cc-11e6-8115-a7c18106d86a", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-78b74f30-f9cd-11e6-8115-a7c18106d86a", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "system-341ffe70-f9ce-11e6-8115-a7c18106d86a", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-3cec3eb0-f9d3-11e6-8a3e-2b904044ea1d", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "system-62439dc0-f9c9-11e6-a747-6121780e0414", + "name": "5:panel_5", + "type": "search" + }, + { + "id": "system-327417e0-8462-11e7-bab8-bd2f0fb42c54", + "name": "6:panel_6", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzMwLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4723\",\"4724\"],\"type\":\"phrases\",\"value\":\"4723, 4724\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4723\"}},{\"match_phrase\":{\"event.code\":\"4724\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Password Reset / Changes [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Password Changes\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Password Reset / Changes [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-568a8130-bcde-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzg4LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "Number of processes [Metrics System]", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Processes\",\"field\":\"process.pid\"},\"schema\":\"metric\",\"type\":\"cardinality\"}],\"listeners\":{},\"params\":{\"addLegend\":false,\"addTooltip\":true,\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.process\\\"\"},\"gauge\":{\"autoExtend\":false,\"backStyle\":\"Full\",\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":100}],\"gaugeColorMode\":\"None\",\"gaugeStyle\":\"Full\",\"gaugeType\":\"Metric\",\"invertColors\":false,\"labels\":{\"color\":\"black\",\"show\":true},\"orientation\":\"vertical\",\"percentageMode\":false,\"scale\":{\"color\":\"#333\",\"labels\":false,\"show\":false,\"width\":2},\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"type\":\"simple\",\"useRange\":false,\"verticalSplit\":false},\"type\":\"gauge\"},\"title\":\"Number of processes\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-590a60f0-5d87-11e7-8884-1bb4c3b890e4", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzkwLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "User Event Actions - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"event.action\",\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":25},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"event.code\",\"field\":\"event.code\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"perPage\":10,\"percentageCol\":\"\",\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"User Event Actions - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5c9ee410-9b74-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-324686c0-fefb-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzkzLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4740\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"event.code\":\"4740\"}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Blocked Accounts [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Blocked Accounts\",\"field\":\"user.name\"},\"schema\":\"metric\",\"type\":\"cardinality\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Blocked Accounts [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5d117970-9ffd-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzk0LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4738\"],\"type\":\"phrases\",\"value\":\"4738\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4738\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Changes - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Changes in Users\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Changes - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5d92b100-bce8-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzk1LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4781\"],\"type\":\"phrases\",\"value\":\"4781\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4781\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Renamed - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Renamed Users\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Renamed - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5e19ff80-231c-11ea-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzk3LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4720\"},\"type\":\"phrase\",\"value\":\"4720\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4720\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Created - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Created User\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performer LogonID\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Created - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-5e7f0ed0-bcd2-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "Wzk4LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Password Changes - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(154,196,198,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"da0b4c06-c9e8-4d1f-908f-e7ba18181951\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4723\\\" OR event.code: \\\"4724\\\"\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Password Changes/Reset\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Password Changes - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-60301890-ff1d-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwMCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Network Traffic (Packets) [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"listeners\":{},\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"lucene\",\"query\":\"-system.network.name:l*\"},\"id\":\"da1046f0-faa0-11e6-86b1-cd7735ff7e23\",\"index_pattern\":\"*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(0,156,224,1)\",\"fill\":\"1\",\"formatter\":\"0.[00]a\",\"id\":\"da1046f1-faa0-11e6-86b1-cd7735ff7e23\",\"label\":\"Inbound\",\"line_width\":\"0\",\"metrics\":[{\"field\":\"system.network.in.packets\",\"id\":\"da1046f2-faa0-11e6-86b1-cd7735ff7e23\",\"type\":\"max\"},{\"field\":\"da1046f2-faa0-11e6-86b1-cd7735ff7e23\",\"id\":\"f41f9280-faa0-11e6-86b1-cd7735ff7e23\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"field\":\"f41f9280-faa0-11e6-86b1-cd7735ff7e23\",\"id\":\"c0da3d80-1b93-11e7-8ada-3df93aab833e\",\"type\":\"positive_only\",\"unit\":\"\"},{\"function\":\"sum\",\"id\":\"ecaad010-2c2c-11e7-be71-3162da85303f\",\"type\":\"series_agg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"value_template\":\"{{value}}/s\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(250,40,255,1)\",\"fill\":\"1\",\"formatter\":\"0.[00]a\",\"id\":\"fbbd5720-faa0-11e6-86b1-cd7735ff7e23\",\"label\":\"Outbound\",\"line_width\":\"0\",\"metrics\":[{\"field\":\"system.network.out.packets\",\"id\":\"fbbd7e30-faa0-11e6-86b1-cd7735ff7e23\",\"type\":\"max\"},{\"field\":\"fbbd7e30-faa0-11e6-86b1-cd7735ff7e23\",\"id\":\"fbbd7e31-faa0-11e6-86b1-cd7735ff7e23\",\"type\":\"derivative\",\"unit\":\"1s\"},{\"id\":\"17e597a0-faa1-11e6-86b1-cd7735ff7e23\",\"script\":\"params.rate != null && params.rate > 0 ? params.rate * -1 : null\",\"type\":\"calculation\",\"variables\":[{\"field\":\"fbbd7e31-faa0-11e6-86b1-cd7735ff7e23\",\"id\":\"1940bad0-faa1-11e6-86b1-cd7735ff7e23\",\"name\":\"rate\"}]},{\"function\":\"sum\",\"id\":\"fe5fbdc0-2c2c-11e7-be71-3162da85303f\",\"type\":\"series_agg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"value_template\":\"{{value}}/s\"}],\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"Mericbeat: Network Traffic (Packets)\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-6b7b9a40-faa1-11e6-86b1-cd7735ff7e23", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwMSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4725\"},\"type\":\"phrase\",\"value\":\"4725\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4725\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Disabled - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Disabled User\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performer LogonId\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Disabled - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-8f20c950-bcd4-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExNywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4726\"},\"type\":\"phrase\",\"value\":\"4726\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4726\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Deleted - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Deleted User\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performed LogonId\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Deleted - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-ee0319a0-bcd4-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2OSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4723\",\"4724\"],\"type\":\"phrases\",\"value\":\"4723, 4724\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4723\"}},{\"match_phrase\":{\"event.code\":\"4724\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Password Changes - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Password Change to\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performer LogonId\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Password Changes - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-da5ffe40-bcd9-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2MiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4767\"},\"type\":\"phrase\",\"value\":\"4767\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4767\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Unlocked Users - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Unlocked User\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performer Logonid\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Unlocked Users - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-da2110c0-bcea-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2MSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4738\"},\"type\":\"phrase\",\"value\":\"4738\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4738\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Changes Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Changed User\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performer LogonId\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Changes Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-abf96c10-bcea-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0MSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Disabled - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(79,147,150,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"52c53f30-6062-4bff-8d18-0abd2c44cd1f\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"((data_stream.dataset:windows.security OR data_stream.dataset:system.security) AND event.code: \\\"4725\\\")\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Disabled\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Disabled - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-97c70300-ff1c-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExOSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Enabled - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(203,142,136,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"2c568de7-ca90-47f1-913a-f4728ba190e4\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4722\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Enabled\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Enabled - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-bf45dc50-ff1a-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Deleted - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(228,155,75,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"af36e28c-22c0-4ede-a00c-43edcc18b5a4\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4726\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Deleted\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Deleted - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-7322f9f0-ff1c-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwNCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Created - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(181,99,93,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"6e4f056d-a44a-4086-993e-ddbe2fe9fb5f\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4720\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Created\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Created - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-d3a5fec0-ff18-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1OCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users locked Out - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(102,102,102,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"5aa7556c-078c-452e-9c4a-df9014319d03\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"((data_stream.dataset:windows.security OR data_stream.dataset:system.security) AND event.code: \\\"4740\\\")\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Locked Out\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users locked Out - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-9dd22440-ff1d-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyMiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Changes TS VB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(221,186,64,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"6a908c02-e05e-4dd3-aea5-2bd9205f7bf2\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4738\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Changes\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Changes TS VB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-c9d959f0-ff1d-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1MiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4781\"},\"type\":\"phrase\",\"value\":\"4781\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4781\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Renamed - Table [Windows System Security]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Old User Name\",\"field\":\"winlog.event_data.OldTargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":100},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Performed by\",\"field\":\"winlog.event_data.SubjectUserName\",\"missingBucket\":true,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"customLabel\":\"Performer LogonId\",\"field\":\"winlog.logon.id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}},{\"accessor\":2,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":3,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Users Renamed - Table [Windows System Security]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-fa876300-231a-11ea-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE3MywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Dashboard links [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":12,\"markdown\":\"[Windows Overview](#/dashboard/system-Windows-Dashboard) | [User Logon Information](#/dashboard/system-bae11b00-9bfc-11ea-87e4-49f31ec44891) | [Logon Failed and Account Lockout](#/dashboard/system-d401ef40-a7d5-11e9-a422-d144027429da) | [User Management Events](#/dashboard/system-71f720f0-ff18-11e9-8405-516218e3d268) | [Group Management Events](#/dashboard/system-bb858830-f412-11e9-8405-516218e3d268)\",\"openLinksInNewTab\":false},\"title\":\"Dashboard links [Windows System Security]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-a3c3f350-9b6d-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzMywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "User Management Events - Affected Users vs Actions - Heatmap [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Target User\",\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":20},\"schema\":\"segment\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":10},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"colorSchema\":\"Blues\",\"colorsNumber\":4,\"colorsRange\":[],\"enableHover\":false,\"invertColors\":false,\"legendPosition\":\"right\",\"percentageMode\":false,\"setColorRange\":false,\"times\":[],\"type\":\"heatmap\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"color\":\"black\",\"overwriteColor\":false,\"rotate\":0,\"show\":true},\"scale\":{\"defaultYExtents\":false,\"type\":\"linear\"},\"show\":false,\"type\":\"value\"}]},\"title\":\"User Management Events - Affected Users vs Actions - Heatmap [Windows System Security]\",\"type\":\"heatmap\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-aa31c9d0-9b75-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-324686c0-fefb-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzNywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Event Distribution in time [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"drop_partials\":false,\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"scaleMetricValues\":false,\"timeRange\":{\"from\":\"now-7d\",\"to\":\"now\"},\"useNormalizedEsInterval\":true},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"event.action\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":15},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"filter\":true,\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false},\"labels\":{\"show\":false},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"mode\":\"stacked\",\"show\":true,\"showCircles\":true,\"type\":\"histogram\",\"valueAxis\":\"ValueAxis-1\",\"circlesRadius\":1}],\"thresholdLine\":{\"color\":\"#E7664C\",\"show\":false,\"style\":\"full\",\"value\":10,\"width\":1},\"times\":[],\"type\":\"histogram\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"title\":\"Event Distribution in time [Windows System Security]\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-caf4d2b0-9b76-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-324686c0-fefb-11e9-8405-516218e3d268", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1MywxXQ==" +} + +{ + "attributes": { + "description": "User management activity.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"1\",\"w\":17,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_1\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"3\",\"w\":9,\"x\":0,\"y\":56},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Created Users [Windows System Security]\",\"panelRefName\":\"panel_3\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"5\",\"w\":9,\"x\":9,\"y\":56},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Enabled Users [Windows System Security]\",\"panelRefName\":\"panel_5\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"6\",\"w\":9,\"x\":0,\"y\":79},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Disabled Users [Windows System Security]\",\"panelRefName\":\"panel_6\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"7\",\"w\":9,\"x\":18,\"y\":56},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Deleted Users [Windows System Security]\",\"panelRefName\":\"panel_7\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"9\",\"w\":9,\"x\":18,\"y\":79},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Passwords Changes [Windows System Security]\",\"panelRefName\":\"panel_9\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"15\",\"w\":9,\"x\":9,\"y\":79},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Unlocked Users [Windows System Security]\",\"panelRefName\":\"panel_15\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"16\",\"w\":9,\"x\":18,\"y\":102},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Users Changes [Windows System Security]\",\"panelRefName\":\"panel_16\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"20\",\"w\":9,\"x\":0,\"y\":102},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Locked-out Users [Windows System Security]\",\"panelRefName\":\"panel_20\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":46,\"i\":\"22\",\"w\":21,\"x\":27,\"y\":72},\"panelIndex\":\"22\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_22\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":19,\"i\":\"23\",\"w\":48,\"x\":0,\"y\":118},\"panelIndex\":\"23\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_23\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"24\",\"w\":9,\"x\":0,\"y\":72},\"panelIndex\":\"24\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_24\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"25\",\"w\":9,\"x\":9,\"y\":49},\"panelIndex\":\"25\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_25\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"26\",\"w\":9,\"x\":18,\"y\":49},\"panelIndex\":\"26\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_26\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"27\",\"w\":9,\"x\":0,\"y\":49},\"panelIndex\":\"27\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_27\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"28\",\"w\":9,\"x\":9,\"y\":72},\"panelIndex\":\"28\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_28\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"29\",\"w\":9,\"x\":18,\"y\":72},\"panelIndex\":\"29\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_29\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"30\",\"w\":9,\"x\":0,\"y\":95},\"panelIndex\":\"30\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_30\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"31\",\"w\":9,\"x\":18,\"y\":95},\"panelIndex\":\"31\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_31\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"32\",\"w\":9,\"x\":9,\"y\":95},\"panelIndex\":\"32\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_32\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"33\",\"w\":9,\"x\":9,\"y\":102},\"panelIndex\":\"33\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_33\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"cf0adfac-7cf2-479d-8ddb-1edeee62d37c\",\"w\":31,\"x\":17,\"y\":0},\"panelIndex\":\"cf0adfac-7cf2-479d-8ddb-1edeee62d37c\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_cf0adfac-7cf2-479d-8ddb-1edeee62d37c\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"a2871661-98a8-489b-b615-e66ebe3b971a\",\"w\":17,\"x\":0,\"y\":8},\"panelIndex\":\"a2871661-98a8-489b-b615-e66ebe3b971a\",\"embeddableConfig\":{\"colors\":{\"added-user-account\":\"#447EBC\",\"deleted-user-account\":\"#82B5D8\",\"disabled-user-account\":\"#82B5D8\",\"enabled-user-account\":\"#0A50A1\",\"modified-user-account\":\"#2F575E\",\"renamed-user-account\":\"#1F78C1\",\"reset-password\":\"#5195CE\"},\"vis\":{\"colors\":{\"added-user-account\":\"#447EBC\",\"deleted-user-account\":\"#82B5D8\",\"disabled-user-account\":\"#82B5D8\",\"enabled-user-account\":\"#0A50A1\",\"modified-user-account\":\"#2F575E\",\"renamed-user-account\":\"#1F78C1\",\"reset-password\":\"#5195CE\",\"unlocked-user-account\":\"#64B0C8\"}},\"enhancements\":{}},\"panelRefName\":\"panel_a2871661-98a8-489b-b615-e66ebe3b971a\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"e80fae4a-6087-41e1-b4b9-31802cb1e4bf\",\"w\":18,\"x\":30,\"y\":8},\"panelIndex\":\"e80fae4a-6087-41e1-b4b9-31802cb1e4bf\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_e80fae4a-6087-41e1-b4b9-31802cb1e4bf\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"dd3e12e6-0d3c-448e-b0c4-91f7dc8742b6\",\"w\":13,\"x\":17,\"y\":8},\"panelIndex\":\"dd3e12e6-0d3c-448e-b0c4-91f7dc8742b6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_dd3e12e6-0d3c-448e-b0c4-91f7dc8742b6\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":25,\"i\":\"29f54335-78db-4c49-a3e0-a641fd0099f6\",\"w\":48,\"x\":0,\"y\":24},\"panelIndex\":\"29f54335-78db-4c49-a3e0-a641fd0099f6\",\"embeddableConfig\":{\"vis\":null,\"enhancements\":{}},\"title\":\"Actions performed over Users [Windows System Security]\",\"panelRefName\":\"panel_29f54335-78db-4c49-a3e0-a641fd0099f6\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":23,\"i\":\"1ec8b993-9ac1-4c7f-b7f7-5136f2e310aa\",\"w\":21,\"x\":27,\"y\":49},\"panelIndex\":\"1ec8b993-9ac1-4c7f-b7f7-5136f2e310aa\",\"embeddableConfig\":{\"colors\":{\"added-user-account\":\"#0A437C\",\"deleted-user-account\":\"#5195CE\",\"enabled-user-account\":\"#0A50A1\",\"modified-user-account\":\"#052B51\",\"renamed-user-account\":\"#1F78C1\",\"reset-password\":\"#5195CE\"},\"vis\":{\"colors\":{\"added-user-account\":\"#0A437C\",\"deleted-user-account\":\"#5195CE\",\"disabled-user-account\":\"#82B5D8\",\"enabled-user-account\":\"#0A50A1\",\"modified-user-account\":\"#052B51\",\"renamed-user-account\":\"#1F78C1\",\"reset-password\":\"#5195CE\"}},\"enhancements\":{}},\"panelRefName\":\"panel_1ec8b993-9ac1-4c7f-b7f7-5136f2e310aa\"}]", + "timeRestore": false, + "title": "[System Windows Security] User Management Events", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-71f720f0-ff18-11e9-8405-516218e3d268", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-2dc6b820-b9e8-11e9-b6a2-c9b4015c4baf", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-5e7f0ed0-bcd2-11e9-b6a2-c9b4015c4baf", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-0620c3d0-bcd4-11e9-b6a2-c9b4015c4baf", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "system-8f20c950-bcd4-11e9-b6a2-c9b4015c4baf", + "name": "6:panel_6", + "type": "visualization" + }, + { + "id": "system-ee0319a0-bcd4-11e9-b6a2-c9b4015c4baf", + "name": "7:panel_7", + "type": "visualization" + }, + { + "id": "system-da5ffe40-bcd9-11e9-b6a2-c9b4015c4baf", + "name": "9:panel_9", + "type": "visualization" + }, + { + "id": "system-da2110c0-bcea-11e9-b6a2-c9b4015c4baf", + "name": "15:panel_15", + "type": "visualization" + }, + { + "id": "system-abf96c10-bcea-11e9-b6a2-c9b4015c4baf", + "name": "16:panel_16", + "type": "visualization" + }, + { + "id": "system-4ac8f5f0-bcfe-11e9-b6a2-c9b4015c4baf", + "name": "20:panel_20", + "type": "visualization" + }, + { + "id": "system-7e178c80-fee1-11e9-8405-516218e3d268", + "name": "22:panel_22", + "type": "search" + }, + { + "id": "system-324686c0-fefb-11e9-8405-516218e3d268", + "name": "23:panel_23", + "type": "search" + }, + { + "id": "system-97c70300-ff1c-11e9-8405-516218e3d268", + "name": "24:panel_24", + "type": "visualization" + }, + { + "id": "system-bf45dc50-ff1a-11e9-8405-516218e3d268", + "name": "25:panel_25", + "type": "visualization" + }, + { + "id": "system-7322f9f0-ff1c-11e9-8405-516218e3d268", + "name": "26:panel_26", + "type": "visualization" + }, + { + "id": "system-d3a5fec0-ff18-11e9-8405-516218e3d268", + "name": "27:panel_27", + "type": "visualization" + }, + { + "id": "system-1b6725f0-ff1d-11e9-8405-516218e3d268", + "name": "28:panel_28", + "type": "visualization" + }, + { + "id": "system-60301890-ff1d-11e9-8405-516218e3d268", + "name": "29:panel_29", + "type": "visualization" + }, + { + "id": "system-9dd22440-ff1d-11e9-8405-516218e3d268", + "name": "30:panel_30", + "type": "visualization" + }, + { + "id": "system-c9d959f0-ff1d-11e9-8405-516218e3d268", + "name": "31:panel_31", + "type": "visualization" + }, + { + "id": "system-1f271bc0-231a-11ea-8405-516218e3d268", + "name": "32:panel_32", + "type": "visualization" + }, + { + "id": "system-fa876300-231a-11ea-8405-516218e3d268", + "name": "33:panel_33", + "type": "visualization" + }, + { + "id": "system-a3c3f350-9b6d-11ea-87e4-49f31ec44891", + "name": "cf0adfac-7cf2-479d-8ddb-1edeee62d37c:panel_cf0adfac-7cf2-479d-8ddb-1edeee62d37c", + "type": "visualization" + }, + { + "id": "system-26877510-9b72-11ea-87e4-49f31ec44891", + "name": "a2871661-98a8-489b-b615-e66ebe3b971a:panel_a2871661-98a8-489b-b615-e66ebe3b971a", + "type": "visualization" + }, + { + "id": "system-117f5a30-9b71-11ea-87e4-49f31ec44891", + "name": "e80fae4a-6087-41e1-b4b9-31802cb1e4bf:panel_e80fae4a-6087-41e1-b4b9-31802cb1e4bf", + "type": "visualization" + }, + { + "id": "system-5c9ee410-9b74-11ea-87e4-49f31ec44891", + "name": "dd3e12e6-0d3c-448e-b0c4-91f7dc8742b6:panel_dd3e12e6-0d3c-448e-b0c4-91f7dc8742b6", + "type": "visualization" + }, + { + "id": "system-aa31c9d0-9b75-11ea-87e4-49f31ec44891", + "name": "29f54335-78db-4c49-a3e0-a641fd0099f6:panel_29f54335-78db-4c49-a3e0-a641fd0099f6", + "type": "visualization" + }, + { + "id": "system-caf4d2b0-9b76-11ea-87e4-49f31ec44891", + "name": "1ec8b993-9ac1-4c7f-b7f7-5136f2e310aa:panel_1ec8b993-9ac1-4c7f-b7f7-5136f2e310aa", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzMxLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4625\",\"4771\"],\"type\":\"phrases\",\"value\":\"4625, 4771\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4625\"}},{\"match_phrase\":{\"event.code\":\"4771\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Logon Failed Acconts [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"user.name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":10},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"bucket\":{\"accessor\":0,\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"type\":\"vis_dimension\"},\"maxFontSize\":37,\"metric\":{\"accessor\":1,\"format\":{\"id\":\"string\",\"params\":{}},\"type\":\"vis_dimension\"},\"minFontSize\":15,\"orientation\":\"single\",\"scale\":\"linear\",\"showLabel\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"title\":\"Logon Failed Acconts [Windows System Security]\",\"type\":\"tagcloud\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-729443b0-a7d6-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwMywxXQ==" +} + +{ + "attributes": { + "columns": [ + "event.action", + "user.name", + "related.user", + "user.domain", + "source.domain", + "source.ip", + "winlog.event_data.SubjectUserName" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4625\",\"4740\"],\"type\":\"phrases\",\"value\":\"4625, 4740\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4625\"}},{\"match_phrase\":{\"event.code\":\"4740\"}}]}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\",\"key\":\"winlog.provider_name\",\"negate\":false,\"params\":{\"query\":\"Microsoft-Windows-Security-Auditing\"},\"type\":\"phrase\"},\"query\":{\"match_phrase\":{\"winlog.provider_name\":\"Microsoft-Windows-Security-Auditing\"}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"},\"version\":true}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "3. Login Failed Details", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-757510b0-a87f-11e9-a422-d144027429da", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4NSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Memory Usage [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.memory\\\"\"},\"id\":\"32f46f40-1b16-11e7-b09e-037021c4f8df\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(211,49,21,1)\",\"fill\":\"1\",\"formatter\":\"bytes\",\"id\":\"4ff61fd0-1b16-11e7-b09e-037021c4f8df\",\"label\":\"Used\",\"line_width\":1,\"metrics\":[{\"field\":\"system.memory.actual.used.bytes\",\"id\":\"4ff61fd1-1b16-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(0,156,224,1)\",\"fill\":\"1\",\"formatter\":\"bytes\",\"id\":\"753a6080-1b16-11e7-b09e-037021c4f8df\",\"label\":\"Cache\",\"line_width\":1,\"metrics\":[{\"field\":\"system.memory.actual.used.bytes\",\"id\":\"753a6081-1b16-11e7-b09e-037021c4f8df\",\"type\":\"avg\"},{\"field\":\"system.memory.used.bytes\",\"id\":\"7c9d3f00-1b16-11e7-b09e-037021c4f8df\",\"type\":\"avg\"},{\"id\":\"869cc160-1b16-11e7-b09e-037021c4f8df\",\"script\":\"params.actual != null && params.used != null ? params.used - params.actual : null\",\"type\":\"calculation\",\"variables\":[{\"field\":\"753a6081-1b16-11e7-b09e-037021c4f8df\",\"id\":\"890f9620-1b16-11e7-b09e-037021c4f8df\",\"name\":\"actual\"},{\"field\":\"7c9d3f00-1b16-11e7-b09e-037021c4f8df\",\"id\":\"8f3ab7f0-1b16-11e7-b09e-037021c4f8df\",\"name\":\"used\"}]}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":\"1\",\"formatter\":\"bytes\",\"id\":\"32f46f41-1b16-11e7-b09e-037021c4f8df\",\"label\":\"Free\",\"line_width\":1,\"metrics\":[{\"field\":\"system.memory.free\",\"id\":\"32f46f42-1b16-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"Memory Usage [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-bfa5e400-1b16-11e7-b09e-037021c4f8df", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0NywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Top Processes By CPU [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"bar_color_rules\":[{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"60e11be0-1b18-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0},{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"f251b035-c854-4547-8128-941757fba9b7\",\"operator\":\"empty\",\"value\":null}],\"drilldown_url\":\"\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.process\\\"\"},\"id\":\"5f5b8d50-1b18-11e7-b09e-037021c4f8df\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"5f5b8d51-1b18-11e7-b09e-037021c4f8df\",\"line_width\":1,\"metrics\":[{\"field\":\"system.process.cpu.total.pct\",\"id\":\"5f5b8d52-1b18-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"process.name\",\"terms_order_by\":\"5f5b8d52-1b18-11e7-b09e-037021c4f8df\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"top_n\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Top Processes By CPU [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-e0f001c0-1b18-11e7-b09e-037021c4f8df", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2NCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "CPU Usage [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.cpu\\\"\"},\"id\":\"80a04950-1b19-11e7-b09e-037021c4f8df\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":\"1\",\"formatter\":\"percent\",\"id\":\"80a04951-1b19-11e7-b09e-037021c4f8df\",\"label\":\"user\",\"line_width\":1,\"metrics\":[{\"field\":\"system.cpu.user.norm.pct\",\"id\":\"80a04952-1b19-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(211,49,21,1)\",\"fill\":\"1\",\"formatter\":\"percent\",\"id\":\"993acf30-1b19-11e7-b09e-037021c4f8df\",\"label\":\"system\",\"line_width\":1,\"metrics\":[{\"field\":\"system.cpu.system.norm.pct\",\"id\":\"993acf31-1b19-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(123,100,255,1)\",\"fill\":\"1\",\"formatter\":\"percent\",\"id\":\"65ca35e0-1b1a-11e7-b09e-037021c4f8df\",\"label\":\"nice\",\"line_width\":1,\"metrics\":[{\"field\":\"system.cpu.nice.norm.pct\",\"id\":\"65ca5cf0-1b1a-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(226,115,0,1)\",\"fill\":\"1\",\"formatter\":\"percent\",\"id\":\"741b5f20-1b1a-11e7-b09e-037021c4f8df\",\"label\":\"irq\",\"line_width\":1,\"metrics\":[{\"field\":\"system.cpu.irq.norm.pct\",\"id\":\"741b5f21-1b1a-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(176,188,0,1)\",\"fill\":\"1\",\"formatter\":\"percent\",\"id\":\"2efc5d40-1b1a-11e7-b09e-037021c4f8df\",\"label\":\"softirq\",\"line_width\":1,\"metrics\":[{\"field\":\"system.cpu.softirq.norm.pct\",\"id\":\"2efc5d41-1b1a-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"rgba(15,20,25,1)\",\"fill\":\"1\",\"formatter\":\"percent\",\"id\":\"ae644a30-1b19-11e7-b09e-037021c4f8df\",\"label\":\"iowait\",\"line_width\":1,\"metrics\":[{\"field\":\"system.cpu.iowait.norm.pct\",\"id\":\"ae644a31-1b19-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":\"0\",\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"stacked\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"CPU Usage [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-ab2d1e90-1b1a-11e7-b09e-037021c4f8df", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "CPU Usage Gauge [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.cpu\\\"\"},\"gauge_color_rules\":[{\"gauge\":\"rgba(104,188,0,1)\",\"id\":\"4ef2c3b0-1b91-11e7-bec4-a5e9ec5cab8b\",\"operator\":\"gte\",\"value\":0},{\"gauge\":\"rgba(254,146,0,1)\",\"id\":\"e6561ae0-1b91-11e7-bec4-a5e9ec5cab8b\",\"operator\":\"gte\",\"value\":0.7},{\"gauge\":\"rgba(211,49,21,1)\",\"id\":\"ec655040-1b91-11e7-bec4-a5e9ec5cab8b\",\"operator\":\"gte\",\"value\":0.85},{\"gauge\":\"rgba(104,188,0,1)\",\"id\":\"8689f71a-2f11-4f2a-bd34-86dd1181253c\",\"operator\":\"empty\",\"value\":null}],\"gauge_inner_width\":10,\"gauge_max\":\"1\",\"gauge_style\":\"half\",\"gauge_width\":10,\"id\":\"4c9e2550-1b91-11e7-bec4-a5e9ec5cab8b\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"4c9e2551-1b91-11e7-bec4-a5e9ec5cab8b\",\"label\":\"CPU Usage\",\"line_width\":1,\"metrics\":[{\"field\":\"system.cpu.total.norm.pct\",\"id\":\"4c9e2552-1b91-11e7-bec4-a5e9ec5cab8b\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"gauge\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"CPU Usage Gauge [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-83e12df0-1b91-11e7-bec4-a5e9ec5cab8b", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExMSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Memory Usage Gauge [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.memory\\\"\"},\"gauge_color_rules\":[{\"gauge\":\"rgba(104,188,0,1)\",\"id\":\"a0d522e0-1b91-11e7-bec4-a5e9ec5cab8b\",\"operator\":\"gte\",\"value\":0},{\"gauge\":\"rgba(254,146,0,1)\",\"id\":\"b45ad8f0-1b91-11e7-bec4-a5e9ec5cab8b\",\"operator\":\"gte\",\"value\":0.7},{\"gauge\":\"rgba(211,49,21,1)\",\"id\":\"c06e9550-1b91-11e7-bec4-a5e9ec5cab8b\",\"operator\":\"gte\",\"value\":0.85},{\"gauge\":\"rgba(104,188,0,1)\",\"id\":\"50c7a204-ec91-4707-a310-7b9960ce30ba\",\"operator\":\"empty\",\"value\":null}],\"gauge_inner_width\":10,\"gauge_max\":\"1\",\"gauge_style\":\"half\",\"gauge_width\":10,\"id\":\"9f51b730-1b91-11e7-bec4-a5e9ec5cab8b\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"9f51b731-1b91-11e7-bec4-a5e9ec5cab8b\",\"label\":\"Memory Usage\",\"line_width\":1,\"metrics\":[{\"field\":\"system.memory.actual.used.pct\",\"id\":\"9f51b732-1b91-11e7-bec4-a5e9ec5cab8b\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"gauge\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Memory Usage Gauge [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-d3166e80-1b91-11e7-bec4-a5e9ec5cab8b", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1NywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "System Navigation [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":12,\"markdown\":\"[System Overview](#/dashboard/system-Metrics-system-overview) | [Host Overview](#/dashboard/system-79ffd6e0-faa0-11e6-947f-177f697178b8)\"},\"title\":\"System Navigation [Metrics System]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Navigation", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyNSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Memory usage vs total [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"listeners\":{},\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"background_color_rules\":[{\"id\":\"6f7618b0-4d5c-11e7-aa29-87a97a796de6\"}],\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.memory\\\"\"},\"id\":\"6bc65720-4d5c-11e7-aa29-87a97a796de6\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"bytes\",\"id\":\"6bc65721-4d5c-11e7-aa29-87a97a796de6\",\"label\":\"Memory usage\",\"line_width\":1,\"metrics\":[{\"field\":\"system.memory.actual.used.bytes\",\"id\":\"6bc65722-4d5c-11e7-aa29-87a97a796de6\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"bytes\",\"id\":\"b8fe6820-4d5c-11e7-aa29-87a97a796de6\",\"label\":\"Total Memory\",\"line_width\":1,\"metrics\":[{\"field\":\"system.memory.total\",\"id\":\"b8fe6821-4d5c-11e7-aa29-87a97a796de6\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Memory usage vs total\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-d2e80340-4d5c-11e7-aa29-87a97a796de6", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Disk Used [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.fsstat\\\"\"},\"gauge_color_rules\":[{\"gauge\":\"rgba(104,188,0,1)\",\"id\":\"51921d10-4d1d-11e7-b5f2-2b7c1895bf32\",\"operator\":\"gte\",\"value\":0},{\"gauge\":\"rgba(251,158,0,1)\",\"id\":\"f26de750-4d54-11e7-b5f2-2b7c1895bf32\",\"operator\":\"gte\",\"value\":0.7},{\"gauge\":\"rgba(211,49,21,1)\",\"id\":\"fa31d190-4d54-11e7-b5f2-2b7c1895bf32\",\"operator\":\"gte\",\"value\":0.85},{\"gauge\":\"rgba(104,188,0,1)\",\"id\":\"34ef1ce2-fb1b-4a64-8971-cbbcd690ff3c\",\"operator\":\"empty\",\"value\":null}],\"gauge_inner_width\":10,\"gauge_max\":\"1\",\"gauge_style\":\"half\",\"gauge_width\":10,\"id\":\"4e4dc780-4d1d-11e7-b5f2-2b7c1895bf32\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"4e4dee90-4d1d-11e7-b5f2-2b7c1895bf32\",\"label\":\"Disk used\",\"line_width\":1,\"metrics\":[{\"agg_with\":\"avg\",\"field\":\"system.fsstat.total_size.used\",\"id\":\"4e4dee91-4d1d-11e7-b5f2-2b7c1895bf32\",\"order\":\"desc\",\"order_by\":\"@timestamp\",\"size\":1,\"type\":\"top_hit\"},{\"agg_with\":\"avg\",\"field\":\"system.fsstat.total_size.total\",\"id\":\"57c96ee0-4d54-11e7-b5f2-2b7c1895bf32\",\"order\":\"desc\",\"order_by\":\"@timestamp\",\"size\":1,\"type\":\"top_hit\"},{\"id\":\"6304cca0-4d54-11e7-b5f2-2b7c1895bf32\",\"script\":\"params.used/params.total \",\"type\":\"math\",\"variables\":[{\"field\":\"4e4dee91-4d1d-11e7-b5f2-2b7c1895bf32\",\"id\":\"6da10430-4d54-11e7-b5f2-2b7c1895bf32\",\"name\":\"used\"},{\"field\":\"57c96ee0-4d54-11e7-b5f2-2b7c1895bf32\",\"id\":\"73b8c510-4d54-11e7-b5f2-2b7c1895bf32\",\"name\":\"total\"}]}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"gauge\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Disk used [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-825fdb80-4d1d-11e7-b5f2-2b7c1895bf32", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExMCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Packetloss [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"background_color_rules\":[{\"id\":\"6ba9b1f0-4d5d-11e7-aa29-87a97a796de6\"}],\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.network\\\"\"},\"id\":\"6984af10-4d5d-11e7-aa29-87a97a796de6\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"6984af11-4d5d-11e7-aa29-87a97a796de6\",\"label\":\"In Packetloss\",\"line_width\":1,\"metrics\":[{\"field\":\"system.network.in.dropped\",\"id\":\"6984af12-4d5d-11e7-aa29-87a97a796de6\",\"type\":\"max\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"},{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"ac2e6b30-4d5d-11e7-aa29-87a97a796de6\",\"label\":\"Out Packetloss\",\"line_width\":1,\"metrics\":[{\"field\":\"system.network.out.dropped\",\"id\":\"ac2e6b31-4d5d-11e7-aa29-87a97a796de6\",\"type\":\"max\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Packetloss [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-96976150-4d5d-11e7-aa29-87a97a796de6", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Interfaces by Incoming traffic [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"bar_color_rules\":[{\"id\":\"44596d40-4d60-11e7-9a4c-ed99bbcaa42b\"}],\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.network\\\"\"},\"id\":\"42ceae90-4d60-11e7-9a4c-ed99bbcaa42b\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"bytes\",\"id\":\"42ced5a0-4d60-11e7-9a4c-ed99bbcaa42b\",\"label\":\"Interfaces by Incoming traffic\",\"line_width\":1,\"metrics\":[{\"field\":\"system.network.in.bytes\",\"id\":\"42ced5a1-4d60-11e7-9a4c-ed99bbcaa42b\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"terms_order_by\":\"42ced5a1-4d60-11e7-9a4c-ed99bbcaa42b\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"top_n\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Interfaces by Incoming traffic [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-99381c80-4d60-11e7-9a4c-ed99bbcaa42b", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyMSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Interfaces by Outgoing traffic [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"bar_color_rules\":[{\"id\":\"9db20be0-4d60-11e7-9a4c-ed99bbcaa42b\"}],\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.network\\\"\"},\"id\":\"9cdba910-4d60-11e7-9a4c-ed99bbcaa42b\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"bytes\",\"id\":\"9cdba911-4d60-11e7-9a4c-ed99bbcaa42b\",\"label\":\"Interfaces by Outgoing traffic\",\"line_width\":1,\"metrics\":[{\"field\":\"system.network.out.bytes\",\"id\":\"9cdba912-4d60-11e7-9a4c-ed99bbcaa42b\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"system.network.name\",\"terms_order_by\":\"9cdba912-4d60-11e7-9a4c-ed99bbcaa42b\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"top_n\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Interfaces by Outgoing traffic [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-c5e3cf90-4d60-11e7-9a4c-ed99bbcaa42b", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1MCwxXQ==" +} + +{ + "attributes": { + "description": "Overview of host metrics", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":55},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":25},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":24,\"y\":55},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":0,\"y\":40},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":24,\"y\":70},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":0,\"y\":70},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":0,\"y\":25},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":40},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"9\",\"w\":8,\"x\":16,\"y\":5},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"10\",\"w\":8,\"x\":0,\"y\":5},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"11\",\"w\":8,\"x\":8,\"y\":5},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"12\",\"w\":8,\"x\":24,\"y\":5},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"13\",\"w\":8,\"x\":32,\"y\":5},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"14\",\"w\":16,\"x\":32,\"y\":15},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":5,\"i\":\"16\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"21\",\"w\":8,\"x\":0,\"y\":15},\"panelIndex\":\"21\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_21\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"22\",\"w\":8,\"x\":8,\"y\":15},\"panelIndex\":\"22\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_22\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"23\",\"w\":8,\"x\":24,\"y\":15},\"panelIndex\":\"23\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_23\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"25\",\"w\":8,\"x\":40,\"y\":5},\"panelIndex\":\"25\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_25\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"27\",\"w\":24,\"x\":0,\"y\":85},\"panelIndex\":\"27\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_27\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"28\",\"w\":24,\"x\":24,\"y\":85},\"panelIndex\":\"28\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_28\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"29\",\"w\":8,\"x\":16,\"y\":15},\"panelIndex\":\"29\",\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}},\"enhancements\":{}},\"panelRefName\":\"panel_29\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":5,\"i\":\"30\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"30\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_30\"}]", + "timeRestore": false, + "title": "[Metrics System] Host overview", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-79ffd6e0-faa0-11e6-947f-177f697178b8", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-6b7b9a40-faa1-11e6-86b1-cd7735ff7e23", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-4d546850-1b15-11e7-b09e-037021c4f8df", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "system-089b85d0-1b16-11e7-b09e-037021c4f8df", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-bfa5e400-1b16-11e7-b09e-037021c4f8df", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "system-e0f001c0-1b18-11e7-b09e-037021c4f8df", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "system-2e224660-1b19-11e7-b09e-037021c4f8df", + "name": "6:panel_6", + "type": "visualization" + }, + { + "id": "system-ab2d1e90-1b1a-11e7-b09e-037021c4f8df", + "name": "7:panel_7", + "type": "visualization" + }, + { + "id": "system-4e4bb1e0-1b1b-11e7-b09e-037021c4f8df", + "name": "8:panel_8", + "type": "visualization" + }, + { + "id": "system-26732e20-1b91-11e7-bec4-a5e9ec5cab8b", + "name": "9:panel_9", + "type": "visualization" + }, + { + "id": "system-83e12df0-1b91-11e7-bec4-a5e9ec5cab8b", + "name": "10:panel_10", + "type": "visualization" + }, + { + "id": "system-d3166e80-1b91-11e7-bec4-a5e9ec5cab8b", + "name": "11:panel_11", + "type": "visualization" + }, + { + "id": "system-522ee670-1b92-11e7-bec4-a5e9ec5cab8b", + "name": "12:panel_12", + "type": "visualization" + }, + { + "id": "system-1aae9140-1b93-11e7-8ada-3df93aab833e", + "name": "13:panel_13", + "type": "visualization" + }, + { + "id": "system-34f97ee0-1b96-11e7-8ada-3df93aab833e", + "name": "14:panel_14", + "type": "visualization" + }, + { + "id": "system-Navigation", + "name": "16:panel_16", + "type": "visualization" + }, + { + "id": "system-19e123b0-4d5a-11e7-aee5-fdc812cc3bec", + "name": "21:panel_21", + "type": "visualization" + }, + { + "id": "system-d2e80340-4d5c-11e7-aa29-87a97a796de6", + "name": "22:panel_22", + "type": "visualization" + }, + { + "id": "system-825fdb80-4d1d-11e7-b5f2-2b7c1895bf32", + "name": "23:panel_23", + "type": "visualization" + }, + { + "id": "system-96976150-4d5d-11e7-aa29-87a97a796de6", + "name": "25:panel_25", + "type": "visualization" + }, + { + "id": "system-99381c80-4d60-11e7-9a4c-ed99bbcaa42b", + "name": "27:panel_27", + "type": "visualization" + }, + { + "id": "system-c5e3cf90-4d60-11e7-9a4c-ed99bbcaa42b", + "name": "28:panel_28", + "type": "visualization" + }, + { + "id": "system-590a60f0-5d87-11e7-8884-1bb4c3b890e4", + "name": "29:panel_29", + "type": "visualization" + }, + { + "id": "system-3d65d450-a9c3-11e7-af20-67db8aecb295", + "name": "30:panel_30", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzMyLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4740\"},\"type\":\"phrase\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4740\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security \"}}" + }, + "title": "Blocked Accounts Tag [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"winlog.event_data.TargetUserName\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":20},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"bucket\":{\"accessor\":0,\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\",\"parsedUrl\":{\"basePath\":\"/s/siem\",\"origin\":\"https://192.168.1.72:5601\",\"pathname\":\"/s/siem/app/kibana\"}}},\"type\":\"vis_dimension\"},\"maxFontSize\":53,\"metric\":{\"accessor\":1,\"format\":{\"id\":\"string\",\"params\":{}},\"type\":\"vis_dimension\"},\"minFontSize\":18,\"orientation\":\"single\",\"scale\":\"linear\",\"showLabel\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}},\"title\":\"Blocked Accounts Tag [Windows System Security]\",\"type\":\"tagcloud\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-7a329a00-a7d5-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwNiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "Hosts histogram by CPU usage [Metrics System]", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0% - 5%\":\"rgb(247,252,245)\",\"10% - 15%\":\"rgb(116,196,118)\",\"15% - 20%\":\"rgb(35,139,69)\",\"5% - 10%\":\"rgb(199,233,192)\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"CPU usage\",\"field\":\"system.cpu.user.norm.pct\"},\"schema\":\"metric\",\"type\":\"avg\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Hosts\",\"field\":\"host.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":20},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"colorSchema\":\"Greens\",\"colorsNumber\":4,\"colorsRange\":[],\"enableHover\":false,\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.cpu\\\" \"},\"invertColors\":false,\"legendPosition\":\"right\",\"percentageMode\":false,\"setColorRange\":false,\"times\":[],\"type\":\"heatmap\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"color\":\"#555\",\"rotate\":0,\"show\":false},\"scale\":{\"defaultYExtents\":false,\"type\":\"linear\"},\"show\":false,\"type\":\"value\"}]},\"title\":\"Hosts histogram by CPU usage [Metrics System]\",\"type\":\"heatmap\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-7cdb1330-4d1a-11e7-a196-69b9a7a020a9", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEwNywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4722\"},\"type\":\"phrase\",\"value\":\"4722\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4722\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Enabled - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Users Enabled\",\"field\":\"user.name\"},\"schema\":\"metric\",\"type\":\"cardinality\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Enabled - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-855957d0-bcdd-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExNCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":{\"query\":\"4726\"},\"type\":\"phrase\",\"value\":\"4726\"},\"query\":{\"match\":{\"event.code\":{\"query\":\"4726\",\"type\":\"phrase\"}}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Deleted - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Deleted Users\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Deleted - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-c359b020-bcdd-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0OSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4740\"],\"type\":\"phrases\",\"value\":\"4740\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4740\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Users Unlocks - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Users Locked Out\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Users Unlocks - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-84502430-bce8-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExMiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"event.code\",\"negate\":false,\"params\":[\"4767\"],\"type\":\"phrases\",\"value\":\"4767\"},\"query\":{\"bool\":{\"minimum_should_match\":1,\"should\":[{\"match_phrase\":{\"event.code\":\"4767\"}}]}}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Unlocked Users - Simple Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Users Unlocks\"},\"schema\":\"metric\",\"type\":\"count\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"dimensions\":{\"metrics\":[{\"accessor\":0,\"format\":{\"id\":\"number\",\"params\":{}},\"type\":\"vis_dimension\"}]},\"metric\":{\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":10000,\"type\":\"range\"}],\"invertColors\":false,\"labels\":{\"show\":true},\"metricColorMode\":\"None\",\"percentageMode\":false,\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":60,\"labelColor\":false,\"subText\":\"\"},\"useRanges\":false},\"type\":\"metric\"},\"title\":\"Unlocked Users - Simple Metric [Windows System Security]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-ab6f8d80-bce8-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzOSwxXQ==" +} + +{ + "attributes": { + "description": "User management activity with TSVB metrics.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"1\",\"w\":17,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_1\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"3\",\"w\":9,\"x\":0,\"y\":55},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Created Users [Windows System Security]\",\"panelRefName\":\"panel_3\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"5\",\"w\":9,\"x\":9,\"y\":55},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Enabled Users [Windows System Security]\",\"panelRefName\":\"panel_5\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"6\",\"w\":9,\"x\":0,\"y\":80},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Disabled Users [Windows System Security]\",\"panelRefName\":\"panel_6\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"7\",\"w\":9,\"x\":18,\"y\":55},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Deleted Users [Windows System Security]\",\"panelRefName\":\"panel_7\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"9\",\"w\":9,\"x\":18,\"y\":80},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Passwords Changes [Windows System Security]\",\"panelRefName\":\"panel_9\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"10\",\"w\":9,\"x\":0,\"y\":46},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_10\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"11\",\"w\":9,\"x\":9,\"y\":46},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_11\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"12\",\"w\":9,\"x\":18,\"y\":46},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_12\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"13\",\"w\":9,\"x\":0,\"y\":71},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_13\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"14\",\"w\":9,\"x\":18,\"y\":71},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_14\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"15\",\"w\":9,\"x\":9,\"y\":80},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Unlocked Users [Windows System Security]\",\"panelRefName\":\"panel_15\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"16\",\"w\":9,\"x\":18,\"y\":105},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Users Changes [Windows System Security]\",\"panelRefName\":\"panel_16\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"17\",\"w\":9,\"x\":0,\"y\":96},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_17\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"18\",\"w\":9,\"x\":9,\"y\":71},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_18\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"19\",\"w\":9,\"x\":18,\"y\":96},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_19\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"20\",\"w\":9,\"x\":0,\"y\":105},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Locked-out Users [Windows System Security]\",\"panelRefName\":\"panel_20\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":48,\"i\":\"22\",\"w\":21,\"x\":27,\"y\":73},\"panelIndex\":\"22\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_22\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":19,\"i\":\"23\",\"w\":48,\"x\":0,\"y\":121},\"panelIndex\":\"23\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_23\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":9,\"i\":\"24\",\"w\":9,\"x\":9,\"y\":96},\"panelIndex\":\"24\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_24\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"25\",\"w\":9,\"x\":9,\"y\":105},\"panelIndex\":\"25\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_25\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"20adcb1b-cebf-4a75-9bc4-eaeeee626c5e\",\"w\":31,\"x\":17,\"y\":0},\"panelIndex\":\"20adcb1b-cebf-4a75-9bc4-eaeeee626c5e\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_20adcb1b-cebf-4a75-9bc4-eaeeee626c5e\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":19,\"i\":\"8aad73ff-37b1-487a-a3f1-b80b93618ac4\",\"w\":18,\"x\":0,\"y\":7},\"panelIndex\":\"8aad73ff-37b1-487a-a3f1-b80b93618ac4\",\"embeddableConfig\":{\"colors\":{\"added-user-account\":\"#0A437C\",\"deleted-user-account\":\"#82B5D8\",\"enabled-user-account\":\"#0A50A1\",\"modified-user-account\":\"#052B51\",\"renamed-user-account\":\"#1F78C1\",\"reset-password\":\"#5195CE\"},\"vis\":{\"colors\":{\"added-user-account\":\"#0A437C\",\"deleted-user-account\":\"#82B5D8\",\"disabled-user-account\":\"#BADFF4\",\"enabled-user-account\":\"#0A50A1\",\"modified-user-account\":\"#052B51\",\"renamed-user-account\":\"#1F78C1\",\"reset-password\":\"#5195CE\"}},\"enhancements\":{}},\"panelRefName\":\"panel_8aad73ff-37b1-487a-a3f1-b80b93618ac4\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":19,\"i\":\"18cc78ac-3f77-4f54-b351-cb94873cae3f\",\"w\":14,\"x\":18,\"y\":7},\"panelIndex\":\"18cc78ac-3f77-4f54-b351-cb94873cae3f\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18cc78ac-3f77-4f54-b351-cb94873cae3f\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":19,\"i\":\"75f5f1fc-bc7c-4f8f-8e5b-0a52d525aa7d\",\"w\":16,\"x\":32,\"y\":7},\"panelIndex\":\"75f5f1fc-bc7c-4f8f-8e5b-0a52d525aa7d\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_75f5f1fc-bc7c-4f8f-8e5b-0a52d525aa7d\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":20,\"i\":\"f443b5b0-ada7-426f-ae2f-46573f94f24f\",\"w\":48,\"x\":0,\"y\":26},\"panelIndex\":\"f443b5b0-ada7-426f-ae2f-46573f94f24f\",\"embeddableConfig\":{\"vis\":null,\"enhancements\":{}},\"title\":\"Actions performed over Users [Windows System Security]\",\"panelRefName\":\"panel_f443b5b0-ada7-426f-ae2f-46573f94f24f\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":27,\"i\":\"820c0311-d378-49dc-a614-e0fed2254603\",\"w\":21,\"x\":27,\"y\":46},\"panelIndex\":\"820c0311-d378-49dc-a614-e0fed2254603\",\"embeddableConfig\":{\"colors\":{\"added-user-account\":\"#0A437C\",\"deleted-user-account\":\"#82B5D8\",\"disabled-user-account\":\"#BADFF4\",\"enabled-user-account\":\"#0A50A1\",\"modified-user-account\":\"#2F575E\",\"renamed-user-account\":\"#1F78C1\",\"reset-password\":\"#5195CE\"},\"vis\":{\"colors\":{\"added-user-account\":\"#0A437C\",\"deleted-user-account\":\"#82B5D8\",\"disabled-user-account\":\"#BADFF4\",\"enabled-user-account\":\"#0A50A1\",\"modified-user-account\":\"#2F575E\",\"renamed-user-account\":\"#1F78C1\",\"reset-password\":\"#5195CE\",\"unlocked-user-account\":\"#0A437C\"}},\"enhancements\":{}},\"panelRefName\":\"panel_820c0311-d378-49dc-a614-e0fed2254603\"}]", + "timeRestore": false, + "title": "[System Windows Security] User Management Events - Simple Metric", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-8223bed0-b9e9-11e9-b6a2-c9b4015c4baf", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-2dc6b820-b9e8-11e9-b6a2-c9b4015c4baf", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-5e7f0ed0-bcd2-11e9-b6a2-c9b4015c4baf", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-0620c3d0-bcd4-11e9-b6a2-c9b4015c4baf", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "system-8f20c950-bcd4-11e9-b6a2-c9b4015c4baf", + "name": "6:panel_6", + "type": "visualization" + }, + { + "id": "system-ee0319a0-bcd4-11e9-b6a2-c9b4015c4baf", + "name": "7:panel_7", + "type": "visualization" + }, + { + "id": "system-da5ffe40-bcd9-11e9-b6a2-c9b4015c4baf", + "name": "9:panel_9", + "type": "visualization" + }, + { + "id": "system-102efd20-bcdd-11e9-b6a2-c9b4015c4baf", + "name": "10:panel_10", + "type": "visualization" + }, + { + "id": "system-855957d0-bcdd-11e9-b6a2-c9b4015c4baf", + "name": "11:panel_11", + "type": "visualization" + }, + { + "id": "system-c359b020-bcdd-11e9-b6a2-c9b4015c4baf", + "name": "12:panel_12", + "type": "visualization" + }, + { + "id": "system-0cb2d940-bcde-11e9-b6a2-c9b4015c4baf", + "name": "13:panel_13", + "type": "visualization" + }, + { + "id": "system-568a8130-bcde-11e9-b6a2-c9b4015c4baf", + "name": "14:panel_14", + "type": "visualization" + }, + { + "id": "system-da2110c0-bcea-11e9-b6a2-c9b4015c4baf", + "name": "15:panel_15", + "type": "visualization" + }, + { + "id": "system-abf96c10-bcea-11e9-b6a2-c9b4015c4baf", + "name": "16:panel_16", + "type": "visualization" + }, + { + "id": "system-84502430-bce8-11e9-b6a2-c9b4015c4baf", + "name": "17:panel_17", + "type": "visualization" + }, + { + "id": "system-ab6f8d80-bce8-11e9-b6a2-c9b4015c4baf", + "name": "18:panel_18", + "type": "visualization" + }, + { + "id": "system-5d92b100-bce8-11e9-b6a2-c9b4015c4baf", + "name": "19:panel_19", + "type": "visualization" + }, + { + "id": "system-4ac8f5f0-bcfe-11e9-b6a2-c9b4015c4baf", + "name": "20:panel_20", + "type": "visualization" + }, + { + "id": "system-7e178c80-fee1-11e9-8405-516218e3d268", + "name": "22:panel_22", + "type": "search" + }, + { + "id": "system-324686c0-fefb-11e9-8405-516218e3d268", + "name": "23:panel_23", + "type": "search" + }, + { + "id": "system-5e19ff80-231c-11ea-8405-516218e3d268", + "name": "24:panel_24", + "type": "visualization" + }, + { + "id": "system-fa876300-231a-11ea-8405-516218e3d268", + "name": "25:panel_25", + "type": "visualization" + }, + { + "id": "system-d770b040-9b35-11ea-87e4-49f31ec44891", + "name": "20adcb1b-cebf-4a75-9bc4-eaeeee626c5e:panel_20adcb1b-cebf-4a75-9bc4-eaeeee626c5e", + "type": "visualization" + }, + { + "id": "system-26877510-9b72-11ea-87e4-49f31ec44891", + "name": "8aad73ff-37b1-487a-a3f1-b80b93618ac4:panel_8aad73ff-37b1-487a-a3f1-b80b93618ac4", + "type": "visualization" + }, + { + "id": "system-5c9ee410-9b74-11ea-87e4-49f31ec44891", + "name": "18cc78ac-3f77-4f54-b351-cb94873cae3f:panel_18cc78ac-3f77-4f54-b351-cb94873cae3f", + "type": "visualization" + }, + { + "id": "system-117f5a30-9b71-11ea-87e4-49f31ec44891", + "name": "75f5f1fc-bc7c-4f8f-8e5b-0a52d525aa7d:panel_75f5f1fc-bc7c-4f8f-8e5b-0a52d525aa7d", + "type": "visualization" + }, + { + "id": "system-aa31c9d0-9b75-11ea-87e4-49f31ec44891", + "name": "f443b5b0-ada7-426f-ae2f-46573f94f24f:panel_f443b5b0-ada7-426f-ae2f-46573f94f24f", + "type": "visualization" + }, + { + "id": "system-caf4d2b0-9b76-11ea-87e4-49f31ec44891", + "name": "820c0311-d378-49dc-a614-e0fed2254603:panel_820c0311-d378-49dc-a614-e0fed2254603", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzMzLDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Top Hosts By CPU (Realtime) [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"bar_color_rules\":[{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"33349dd0-1b1c-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0},{\"bar_color\":\"rgba(254,146,0,1)\",\"id\":\"997dc440-1b1c-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0.6},{\"bar_color\":\"rgba(211,49,21,1)\",\"id\":\"a10d7f20-1b1c-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0.85},{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"9ec25b95-ed0f-48ba-b481-237d57c82d9e\",\"operator\":\"empty\",\"value\":null}],\"drilldown_url\":\"../app/kibana#/dashboard/system-79ffd6e0-faa0-11e6-947f-177f697178b8?_a=(query:(language:kuery,query:'host.name:\\\"{{key}}\\\"'))\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.cpu\\\"\"},\"id\":\"31e5afa0-1b1c-11e7-b09e-037021c4f8df\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"31e5afa1-1b1c-11e7-b09e-037021c4f8df\",\"line_width\":1,\"metrics\":[{\"field\":\"system.cpu.user.norm.pct\",\"id\":\"31e5afa2-1b1c-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"host.name\",\"terms_order_by\":\"31e5afa2-1b1c-11e7-b09e-037021c4f8df\",\"terms_size\":\"10\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"top_n\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Top Hosts By CPU (Realtime) [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-855899e0-1b1c-11e7-b09e-037021c4f8df", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExMywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "User Logons [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"d5bcde50-9bfc-11ea-aaa3-618beeff2d9c\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(7,139,141,1)\",\"id\":\"16018150-9bfd-11ea-aaa3-618beeff2d9c\",\"operator\":\"gte\",\"value\":0},{\"background_color\":\"rgba(7,139,141,1)\",\"id\":\"064f50dd-4627-409d-864d-ac4cec366698\",\"operator\":\"empty\",\"value\":null}],\"filter\":{\"language\":\"kuery\",\"query\":\"((data_stream.dataset:windows.security OR data_stream.dataset:system.security) AND event.code: \\\"4624\\\")\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Logons \",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"User Logons [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-860706a0-9bfd-11ea-87e4-49f31ec44891", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExNSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Failed Logons TSVB [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(181,99,93,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"372091ac-c8cc-4d56-8a17-ab883fe8b796\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"((data_stream.dataset:windows.security OR data_stream.dataset:system.security) AND event.code: \\\"4625\\\")\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Failed Logon\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Failed Logons TSVB [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-8ef59f90-6ab8-11ea-896f-0d70f7ec3956", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzExNiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"(data_stream.dataset:windows.application OR data_stream.dataset:windows.forwarded OR data_stream.dataset:windows.powershell OR data_stream.dataset:windows.powershell_operational OR data_stream.dataset:windows.security OR data_stream.dataset:windows.sysmon_operational OR data_stream.dataset:windows.system OR data_stream.dataset:system.application OR data_stream.dataset:system.security OR data_stream.dataset:system.system)\"}}" + }, + "title": "Event Levels [Windows Overview]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Log Levels\",\"field\":\"log.level\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":1,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Event Levels [Windows Overview]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Event-Levels", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyNCwxXQ==" +} + +{ + "attributes": { + "columns": [ + "host.hostname", + "process.name", + "message" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlight\":{\"fields\":{\"*\":{}},\"fragment_size\":2147483647,\"post_tags\":[\"@/kibana-highlighted-field@\"],\"pre_tags\":[\"@kibana-highlighted-field@\"],\"require_field_match\":false},\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:system.syslog\"}}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Syslog logs [Logs System]", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Syslog-system-logs", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE4OSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "Syslog events by hostname [Logs System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"host.hostname\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"yAxis\":{}},\"title\":\"Syslog events by hostname\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Syslog-events-by-hostname", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-Syslog-system-logs", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyOSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[]}" + }, + "savedSearchRefName": "search_0", + "title": "Syslog hostnames and processes [Logs System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"host.hostname\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"process.name\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"listeners\":{},\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":true,\"legendPosition\":\"bottom\",\"shareYAxis\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"Syslog hostnames and processes\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Syslog-hostnames-and-processes", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "system-Syslog-system-logs", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzMCwxXQ==" +} + +{ + "attributes": { + "description": "Syslog dashboard from the Logs System integration", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"1\",\"w\":32,\"x\":0,\"y\":4},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":16,\"i\":\"2\",\"w\":16,\"x\":32,\"y\":4},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":28,\"i\":\"3\",\"w\":48,\"x\":0,\"y\":20},\"panelIndex\":\"3\",\"embeddableConfig\":{\"columns\":[\"host.hostname\",\"process.name\",\"message\"],\"sort\":[\"@timestamp\",\"desc\"],\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":4,\"i\":\"4\",\"w\":48,\"x\":0,\"y\":0},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"}]", + "timeRestore": false, + "title": "[Logs System] Syslog dashboard", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Logs-syslog-dashboard", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-Syslog-events-by-hostname", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-Syslog-hostnames-and-processes", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "system-Syslog-system-logs", + "name": "3:panel_3", + "type": "search" + }, + { + "id": "system-327417e0-8462-11e7-bab8-bd2f0fb42c54", + "name": "4:panel_4", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzM0LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"\"}}" + }, + "title": "Number of hosts [Metrics System]", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"customLabel\":\"Number of hosts\",\"field\":\"host.name\"},\"schema\":\"metric\",\"type\":\"cardinality\"}],\"params\":{\"addLegend\":false,\"addTooltip\":true,\"gauge\":{\"autoExtend\":false,\"backStyle\":\"Full\",\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":100}],\"gaugeColorMode\":\"None\",\"gaugeStyle\":\"Full\",\"gaugeType\":\"Metric\",\"invertColors\":false,\"labels\":{\"color\":\"black\",\"show\":false},\"orientation\":\"vertical\",\"percentageMode\":false,\"scale\":{\"color\":\"#333\",\"labels\":false,\"show\":false,\"width\":2},\"style\":{\"bgColor\":false,\"bgFill\":\"#000\",\"fontSize\":\"63\",\"labelColor\":false,\"subText\":\"\"},\"type\":\"simple\",\"useRange\":false,\"verticalSplit\":false},\"type\":\"gauge\"},\"title\":\"Number of hosts [Metrics System]\",\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-c6f2ffd0-4d17-11e7-a196-69b9a7a020a9", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE1MSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Top Hosts By Memory (Realtime) [Metrics System]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"bar_color_rules\":[{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"33349dd0-1b1c-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0},{\"bar_color\":\"rgba(254,146,0,1)\",\"id\":\"997dc440-1b1c-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0.6},{\"bar_color\":\"rgba(211,49,21,1)\",\"id\":\"a10d7f20-1b1c-11e7-b09e-037021c4f8df\",\"operator\":\"gte\",\"value\":0.85},{\"bar_color\":\"rgba(104,188,0,1)\",\"id\":\"fc4672e4-28ae-4d30-aaf5-4051ea74e7ee\",\"operator\":\"empty\",\"value\":null}],\"drilldown_url\":\"../app/kibana#/dashboard/system-79ffd6e0-faa0-11e6-947f-177f697178b8?_a=(query:(language:kuery,query:'host.name:\\\"{{key}}\\\"'))\",\"filter\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset : \\\"system.memory\\\"\"},\"id\":\"31e5afa0-1b1c-11e7-b09e-037021c4f8df\",\"index_pattern\":\"metrics-*\",\"interval\":\"auto\",\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"percent\",\"id\":\"31e5afa1-1b1c-11e7-b09e-037021c4f8df\",\"line_width\":1,\"metrics\":[{\"field\":\"system.memory.actual.used.pct\",\"id\":\"31e5afa2-1b1c-11e7-b09e-037021c4f8df\",\"type\":\"avg\"}],\"point_size\":1,\"seperate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"terms\",\"stacked\":\"none\",\"terms_field\":\"host.name\",\"terms_order_by\":\"31e5afa2-1b1c-11e7-b09e-037021c4f8df\",\"terms_size\":\"10\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"top_n\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"drop_last_bucket\":1},\"title\":\"Top Hosts By Memory (Realtime) [Metrics System]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-fe064790-1b1f-11e7-bec4-a5e9ec5cab8b", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE3NCwxXQ==" +} + +{ + "attributes": { + "description": "Overview of system metrics", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":4,\"i\":\"9\",\"w\":48,\"x\":0,\"y\":0},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"11\",\"w\":8,\"x\":0,\"y\":4},\"panelIndex\":\"11\",\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}},\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":20,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":12},\"panelIndex\":\"12\",\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}},\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":20,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":12},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":24,\"i\":\"14\",\"w\":48,\"x\":0,\"y\":32},\"panelIndex\":\"14\",\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0% - 15%\":\"rgb(247,252,245)\",\"15% - 30%\":\"rgb(199,233,192)\",\"30% - 45%\":\"rgb(116,196,118)\",\"45% - 60%\":\"rgb(35,139,69)\"}},\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"16\",\"w\":8,\"x\":32,\"y\":4},\"panelIndex\":\"16\",\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}},\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"17\",\"w\":8,\"x\":40,\"y\":4},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"18\",\"w\":8,\"x\":24,\"y\":4},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"19\",\"w\":8,\"x\":16,\"y\":4},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.6.0\",\"type\":\"visualization\",\"gridData\":{\"h\":8,\"i\":\"20\",\"w\":8,\"x\":8,\"y\":4},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"}]", + "timeRestore": false, + "title": "[Metrics System] Overview", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Metrics-system-overview", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-Navigation", + "name": "9:panel_9", + "type": "visualization" + }, + { + "id": "system-c6f2ffd0-4d17-11e7-a196-69b9a7a020a9", + "name": "11:panel_11", + "type": "visualization" + }, + { + "id": "system-fe064790-1b1f-11e7-bec4-a5e9ec5cab8b", + "name": "12:panel_12", + "type": "visualization" + }, + { + "id": "system-855899e0-1b1c-11e7-b09e-037021c4f8df", + "name": "13:panel_13", + "type": "visualization" + }, + { + "id": "system-7cdb1330-4d1a-11e7-a196-69b9a7a020a9", + "name": "14:panel_14", + "type": "visualization" + }, + { + "id": "system-522ee670-1b92-11e7-bec4-a5e9ec5cab8b", + "name": "16:panel_16", + "type": "visualization" + }, + { + "id": "system-1aae9140-1b93-11e7-8ada-3df93aab833e", + "name": "17:panel_17", + "type": "visualization" + }, + { + "id": "system-825fdb80-4d1d-11e7-b5f2-2b7c1895bf32", + "name": "18:panel_18", + "type": "visualization" + }, + { + "id": "system-d3166e80-1b91-11e7-bec4-a5e9ec5cab8b", + "name": "19:panel_19", + "type": "visualization" + }, + { + "id": "system-83e12df0-1b91-11e7-bec4-a5e9ec5cab8b", + "name": "20:panel_20", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzM1LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"(data_stream.dataset:windows.application OR data_stream.dataset:windows.forwarded OR data_stream.dataset:windows.powershell OR data_stream.dataset:windows.powershell_operational OR data_stream.dataset:windows.security OR data_stream.dataset:windows.sysmon_operational OR data_stream.dataset:windows.system OR data_stream.dataset:system.application OR data_stream.dataset:system.security OR data_stream.dataset:system.system)\"}}" + }, + "title": "Number of Events [Windows Overview]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"}],\"listeners\":{},\"params\":{\"fontSize\":60},\"type\":\"metric\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Number-of-Events", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyNywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"(data_stream.dataset:windows.application OR data_stream.dataset:windows.forwarded OR data_stream.dataset:windows.powershell OR data_stream.dataset:windows.powershell_operational OR data_stream.dataset:windows.security OR data_stream.dataset:windows.sysmon_operational OR data_stream.dataset:windows.system OR data_stream.dataset:system.application OR data_stream.dataset:system.security OR data_stream.dataset:system.system)\"}}" + }, + "title": "Number of Events Over Time By Channel [Windows Overview]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"drop_partials\":false,\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"timeRange\":{\"from\":\"now-15d\",\"mode\":\"relative\",\"to\":\"now\"},\"useNormalizedEsInterval\":true},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Channel\",\"field\":\"winlog.channel\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":6},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"defaultYExtents\":false,\"dimensions\":{\"series\":[{\"accessor\":1,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"x\":{\"accessor\":0,\"aggType\":\"date_histogram\",\"format\":{\"id\":\"date\",\"params\":{\"pattern\":\"YYYY-MM-DD HH:mm\"}},\"params\":{\"bounds\":{\"max\":\"2019-02-05T04:30:25.961Z\",\"min\":\"2019-01-21T04:30:25.961Z\"},\"date\":true,\"format\":\"YYYY-MM-DD HH:mm\",\"interval\":43200000}},\"y\":[{\"accessor\":2,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"mode\":\"stacked\",\"show\":\"true\",\"type\":\"histogram\",\"valueAxis\":\"ValueAxis-1\",\"circlesRadius\":1}],\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"type\":\"histogram\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"defaultYExtents\":false,\"mode\":\"normal\",\"setYExtents\":false,\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}],\"yAxis\":{},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"title\":\"Number of Events Over Time By Channel [Windows Overview]\",\"type\":\"histogram\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Number-of-Events-Over-Time-By-Event-Log", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyNiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"(data_stream.dataset:windows.application OR data_stream.dataset:windows.forwarded OR data_stream.dataset:windows.powershell OR data_stream.dataset:windows.powershell_operational OR data_stream.dataset:windows.security OR data_stream.dataset:windows.sysmon_operational OR data_stream.dataset:windows.system OR data_stream.dataset:system.application OR data_stream.dataset:system.security OR data_stream.dataset:system.system)\"}}" + }, + "title": "Sources (Provider Names) [Windows Overview]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"winlog.provider_name\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":7},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":false,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"right\",\"shareYAxis\":true,\"type\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"Sources (Provider Names) [Windows Overview]\",\"type\":\"pie\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Sources", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEyOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"(data_stream.dataset:windows.application OR data_stream.dataset:windows.forwarded OR data_stream.dataset:windows.powershell OR data_stream.dataset:windows.powershell_operational OR data_stream.dataset:windows.security OR data_stream.dataset:windows.sysmon_operational OR data_stream.dataset:windows.system OR data_stream.dataset:system.application OR data_stream.dataset:system.security OR data_stream.dataset:system.system)\"}}" + }, + "title": "Top Event IDs [Windows Overview]", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Event IDs\",\"field\":\"winlog.event_id\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"bucket\",\"type\":\"terms\"}],\"params\":{\"dimensions\":{\"buckets\":[{\"accessor\":0,\"aggType\":\"terms\",\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"missingBucketLabel\":\"Missing\",\"otherBucketLabel\":\"Other\"}},\"params\":{}}],\"metrics\":[{\"accessor\":1,\"aggType\":\"count\",\"format\":{\"id\":\"number\"},\"params\":{}}]},\"perPage\":10,\"showMetricsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\",\"showToolbar\":true},\"title\":\"Top Event IDs [Windows Overview]\",\"type\":\"table\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Top-Event-IDs", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "logs-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzMSwxXQ==" +} + +{ + "attributes": { + "description": "Overview of all Windows Event Logs.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"(data_stream.dataset:windows.application OR data_stream.dataset:system.application OR data_stream.dataset:windows.forwarded OR data_stream.dataset:windows.powershell OR data_stream.dataset:windows.powershell_operational OR data_stream.dataset:windows.security OR data_stream.dataset:system.security OR data_stream.dataset:windows.sysmon_operational OR data_stream.dataset:windows.system OR data_stream.dataset:system.system)\"}}" + }, + "optionsJSON": "{\"darkTheme\":false}", + "panelsJSON": "[{\"version\":\"7.0.0-SNAPSHOT\",\"gridData\":{\"h\":20,\"i\":\"1\",\"w\":36,\"x\":12,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_0\"},{\"version\":\"7.0.0-SNAPSHOT\",\"gridData\":{\"h\":20,\"i\":\"3\",\"w\":12,\"x\":0,\"y\":0},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.0.0-SNAPSHOT\",\"gridData\":{\"h\":20,\"i\":\"4\",\"w\":16,\"x\":16,\"y\":20},\"panelIndex\":\"4\",\"embeddableConfig\":{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}},\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.0.0-SNAPSHOT\",\"gridData\":{\"h\":20,\"i\":\"5\",\"w\":16,\"x\":32,\"y\":20},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.0.0-SNAPSHOT\",\"gridData\":{\"h\":20,\"i\":\"6\",\"w\":16,\"x\":0,\"y\":20},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"}]", + "timeRestore": false, + "title": "[System] Windows Overview", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-Windows-Dashboard", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-Number-of-Events-Over-Time-By-Event-Log", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "system-Number-of-Events", + "name": "panel_1", + "type": "visualization" + }, + { + "id": "system-Top-Event-IDs", + "name": "panel_2", + "type": "visualization" + }, + { + "id": "system-Event-Levels", + "name": "panel_3", + "type": "visualization" + }, + { + "id": "system-Sources", + "name": "panel_4", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzM2LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Removed - TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"bfcaced0-f419-11e9-928e-8f5fd2b6c66e\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(228,155,75,1)\",\"id\":\"11604700-9b51-11ea-99a1-e5b989979a59\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"2be39146-314a-4d34-b5d0-1fa7124c7399\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code:4733 OR event.code:4729 OR event.code:4788 OR event.code:4786 OR event.code:4752 OR event.code:4762 OR event.code:4747\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Removed from Group\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Removed - TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-a5f664c0-f49a-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzNCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Blocked Accounts TSVB [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"color\":\"rgba(51,51,51,1)\",\"id\":\"8d597960-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(102,102,102,1)\",\"id\":\"a3f59730-ff18-11e9-8249-2371c695f3b0\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"color\":\"rgba(51,51,51,1)\",\"id\":\"1e782824-bfa4-4468-9805-b55d7c32336f\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4740\\\"\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Blocked Accounts\",\"line_width\":1,\"metrics\":[{\"field\":\"user.name\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"cardinality\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Blocked Accounts TSVB [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-a79395f0-6aba-11ea-896f-0d70f7ec3956", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzNSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Logon Events Timeline [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4672\\\" or event.code: \\\"4624\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_filters\":[{\"color\":\"rgba(226,115,0,1)\",\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4672\\\"\"},\"id\":\"7560ee50-685f-11ea-8d46-c19e41702dd4\",\"label\":\"Admin logons\"},{\"color\":\"rgba(164,221,243,1)\",\"filter\":{\"language\":\"kuery\",\"query\":\"event.code: \\\"4624\\\"\"},\"id\":\"80e7fb10-685f-11ea-8d46-c19e41702dd4\",\"label\":\"Logon Events\"}],\"split_mode\":\"filters\",\"stacked\":\"none\",\"type\":\"timeseries\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"type\":\"timeseries\",\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"title\":\"Logon Events Timeline [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-a909b930-685f-11ea-896f-0d70f7ec3956", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzEzNiwxXQ==" +} + +{ + "attributes": { + "description": "User logon activity dashboard.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":28,\"i\":\"1\",\"w\":18,\"x\":0,\"y\":34},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Admin Users Sessions\",\"panelRefName\":\"panel_1\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":18,\"i\":\"3\",\"w\":18,\"x\":0,\"y\":16},\"panelIndex\":\"3\",\"embeddableConfig\":{\"colors\":{\"AdminLocalSta\":\"#890F02\",\"SERVICIO LOCAL\":\"#508642\"},\"legendOpen\":true,\"vis\":{\"colors\":{\"AdminLocalSta\":\"#890F02\",\"NETWORK SERVICE\":\"#1F78C1\",\"SERVICIO LOCAL\":\"#508642\"},\"legendOpen\":true},\"enhancements\":{}},\"title\":\"Administrators Logged On\",\"panelRefName\":\"panel_3\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":6,\"i\":\"4\",\"w\":12,\"x\":0,\"y\":0},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_4\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":47,\"i\":\"10\",\"w\":23,\"x\":0,\"y\":62},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Details\",\"panelRefName\":\"panel_10\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":6,\"i\":\"34fc9633-8a7c-444d-8d19-06095b55fb43\",\"w\":36,\"x\":12,\"y\":0},\"panelIndex\":\"34fc9633-8a7c-444d-8d19-06095b55fb43\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_34fc9633-8a7c-444d-8d19-06095b55fb43\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"67d2409d-3e51-45d5-972f-32a36537e622\",\"w\":9,\"x\":0,\"y\":6},\"panelIndex\":\"67d2409d-3e51-45d5-972f-32a36537e622\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_67d2409d-3e51-45d5-972f-32a36537e622\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"33d05ce3-f60d-4a31-a668-aa6fab0cc800\",\"w\":9,\"x\":9,\"y\":6},\"panelIndex\":\"33d05ce3-f60d-4a31-a668-aa6fab0cc800\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_33d05ce3-f60d-4a31-a668-aa6fab0cc800\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"7b3906e6-3a81-450c-bb31-ca0d670440b7\",\"w\":30,\"x\":18,\"y\":6},\"panelIndex\":\"7b3906e6-3a81-450c-bb31-ca0d670440b7\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Events Timeline\",\"panelRefName\":\"panel_7b3906e6-3a81-450c-bb31-ca0d670440b7\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"cf50b48e-453c-46fb-ad35-7ccfb7b03de0\",\"w\":15,\"x\":18,\"y\":19},\"panelIndex\":\"cf50b48e-453c-46fb-ad35-7ccfb7b03de0\",\"embeddableConfig\":{\"colors\":{\"CachedInteractive\":\"#6ED0E0\",\"Interactive\":\"#2F575E\",\"Network\":\"#447EBC\",\"RemoteInteractive\":\"#64B0C8\",\"Service\":\"#6ED0E0\",\"Unlock\":\"#BADFF4\"},\"legendOpen\":true,\"vis\":{\"colors\":{\"CachedInteractive\":\"#6ED0E0\",\"Interactive\":\"#2F575E\",\"Network\":\"#447EBC\",\"RemoteInteractive\":\"#64B0C8\",\"Service\":\"#65C5DB\",\"Unlock\":\"#BADFF4\"},\"legendOpen\":true},\"enhancements\":{}},\"title\":\"Logon Types\",\"panelRefName\":\"panel_cf50b48e-453c-46fb-ad35-7ccfb7b03de0\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"a743ffe5-a2ac-4c0b-9b6f-a81563140c42\",\"w\":15,\"x\":33,\"y\":19},\"panelIndex\":\"a743ffe5-a2ac-4c0b-9b6f-a81563140c42\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_a743ffe5-a2ac-4c0b-9b6f-a81563140c42\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":28,\"i\":\"454bb008-9720-455e-8ab9-b2f47d25aa4f\",\"w\":18,\"x\":18,\"y\":34},\"panelIndex\":\"454bb008-9720-455e-8ab9-b2f47d25aa4f\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"RDP Reconnections and Desconnections\",\"panelRefName\":\"panel_454bb008-9720-455e-8ab9-b2f47d25aa4f\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":28,\"i\":\"29a0e70a-ab23-4d48-8d4e-9a39c5af47ad\",\"w\":12,\"x\":36,\"y\":34},\"panelIndex\":\"29a0e70a-ab23-4d48-8d4e-9a39c5af47ad\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_29a0e70a-ab23-4d48-8d4e-9a39c5af47ad\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":46,\"i\":\"28115147-8399-4fcd-95ce-ed0a4f4239e3\",\"w\":25,\"x\":23,\"y\":62},\"panelIndex\":\"28115147-8399-4fcd-95ce-ed0a4f4239e3\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logout Details\",\"panelRefName\":\"panel_28115147-8399-4fcd-95ce-ed0a4f4239e3\"}]", + "timeRestore": false, + "title": "[System Windows Security] User Logons", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-bae11b00-9bfc-11ea-87e4-49f31ec44891", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-804dd400-a248-11e9-a422-d144027429da", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-e2516c10-a249-11e9-a422-d144027429da", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-18348f30-a24d-11e9-a422-d144027429da", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "system-ce71c9a0-a25e-11e9-a422-d144027429da", + "name": "10:panel_10", + "type": "search" + }, + { + "id": "system-a3c3f350-9b6d-11ea-87e4-49f31ec44891", + "name": "34fc9633-8a7c-444d-8d19-06095b55fb43:panel_34fc9633-8a7c-444d-8d19-06095b55fb43", + "type": "visualization" + }, + { + "id": "system-0622da40-9bfd-11ea-87e4-49f31ec44891", + "name": "67d2409d-3e51-45d5-972f-32a36537e622:panel_67d2409d-3e51-45d5-972f-32a36537e622", + "type": "visualization" + }, + { + "id": "system-860706a0-9bfd-11ea-87e4-49f31ec44891", + "name": "33d05ce3-f60d-4a31-a668-aa6fab0cc800:panel_33d05ce3-f60d-4a31-a668-aa6fab0cc800", + "type": "visualization" + }, + { + "id": "system-a909b930-685f-11ea-896f-0d70f7ec3956", + "name": "7b3906e6-3a81-450c-bb31-ca0d670440b7:panel_7b3906e6-3a81-450c-bb31-ca0d670440b7", + "type": "visualization" + }, + { + "id": "system-006d75f0-9c03-11ea-87e4-49f31ec44891", + "name": "cf50b48e-453c-46fb-ad35-7ccfb7b03de0:panel_cf50b48e-453c-46fb-ad35-7ccfb7b03de0", + "type": "visualization" + }, + { + "id": "system-21aadac0-9c0b-11ea-87e4-49f31ec44891", + "name": "a743ffe5-a2ac-4c0b-9b6f-a81563140c42:panel_a743ffe5-a2ac-4c0b-9b6f-a81563140c42", + "type": "visualization" + }, + { + "id": "system-6f4071a0-7a78-11ea-bc9a-0baf2ca323a3", + "name": "454bb008-9720-455e-8ab9-b2f47d25aa4f:panel_454bb008-9720-455e-8ab9-b2f47d25aa4f", + "type": "search" + }, + { + "id": "system-25f31ee0-9c23-11ea-87e4-49f31ec44891", + "name": "29a0e70a-ab23-4d48-8d4e-9a39c5af47ad:panel_29a0e70a-ab23-4d48-8d4e-9a39c5af47ad", + "type": "visualization" + }, + { + "id": "system-06b6b060-7a80-11ea-bc9a-0baf2ca323a3", + "name": "28115147-8399-4fcd-95ce-ed0a4f4239e3:panel_28115147-8399-4fcd-95ce-ed0a4f4239e3", + "type": "search" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzM3LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Users Added - Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"bfcaced0-f419-11e9-928e-8f5fd2b6c66e\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(181,99,93,1)\",\"id\":\"a7d935e0-f497-11e9-928e-8f5fd2b6c66e\",\"operator\":\"gte\",\"value\":1},{\"background_color\":\"rgba(204,204,204,1)\",\"id\":\"6c24d9f3-7e05-4b7d-8885-c6049a2b9c4a\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code:4732 OR event.code:4728 OR event.code:4756 OR event.code:4751 OR event.code:4761 OR event.code:4746 OR event.code:4785 OR event.code:4787\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Users Added to Group\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Users Added - Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-ffebe440-f419-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE3NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Groups Deleted TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(200,201,197,1)\",\"id\":\"bfcaced0-f419-11e9-928e-8f5fd2b6c66e\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(228,155,75,1)\",\"id\":\"a7d935e0-f497-11e9-928e-8f5fd2b6c66e\",\"operator\":\"gt\",\"value\":0},{\"background_color\":\"rgba(200,201,197,1)\",\"id\":\"70eef5cd-190e-4b51-b453-2a7d497a2764\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code:4734 OR event.code:4730 OR event.code:4758 OR event.code:4753 OR event.code:4763 OR event.code:4748 OR event.code:4789 OR event.code:4792\"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Groups Deleted\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Groups Deleted TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-e22c6f40-f498-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE2NywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Groups Created TSVB Metric [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"axis_formatter\":\"number\",\"axis_position\":\"left\",\"axis_scale\":\"normal\",\"background_color_rules\":[{\"background_color\":\"rgba(200,201,197,1)\",\"id\":\"bfcaced0-f419-11e9-928e-8f5fd2b6c66e\",\"operator\":\"lte\",\"value\":0},{\"background_color\":\"rgba(181,99,93,1)\",\"id\":\"a7d935e0-f497-11e9-928e-8f5fd2b6c66e\",\"operator\":\"gt\",\"value\":0},{\"background_color\":\"rgba(200,201,197,1)\",\"id\":\"b50ac1f1-6c2e-4b36-ae34-84f66f2fa29b\",\"operator\":\"empty\",\"value\":null}],\"drop_last_bucket\":0,\"filter\":{\"language\":\"kuery\",\"query\":\"event.code:4731 OR event.code:4727 OR event.code:\\\"4754\\\" OR event.code:\\\"4749\\\" OR event.code:\\\"4759\\\" OR event.code:\\\"4744\\\" OR event.code:\\\"4783\\\" OR event.code:\\\"4790\\\" \"},\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"index_pattern\":\"logs-*\",\"interval\":\"90d\",\"isModelInvalid\":false,\"series\":[{\"axis_position\":\"right\",\"chart_type\":\"line\",\"color\":\"#68BC00\",\"fill\":0.5,\"formatter\":\"number\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"label\":\"Groups Created\",\"line_width\":1,\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"point_size\":1,\"separate_axis\":0,\"split_color_mode\":\"gradient\",\"split_mode\":\"everything\",\"stacked\":\"none\"}],\"show_grid\":1,\"show_legend\":1,\"time_field\":\"@timestamp\",\"time_range_mode\":\"entire_time_range\",\"type\":\"metric\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true},\"title\":\"Groups Created TSVB Metric [Windows System Security]\",\"type\":\"metrics\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-ee292bc0-f499-11e9-8405-516218e3d268", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE3MCwxXQ==" +} + +{ + "attributes": { + "description": "Group management activity.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"22\",\"w\":16,\"x\":0,\"y\":0},\"panelIndex\":\"22\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_22\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"29\",\"w\":16,\"x\":0,\"y\":68},\"panelIndex\":\"29\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_29\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"30\",\"w\":9,\"x\":18,\"y\":48},\"panelIndex\":\"30\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_30\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"31\",\"w\":9,\"x\":0,\"y\":48},\"panelIndex\":\"31\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_31\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"32\",\"w\":9,\"x\":9,\"y\":48},\"panelIndex\":\"32\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_32\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"33\",\"w\":17,\"x\":16,\"y\":68},\"panelIndex\":\"33\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_33\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"34\",\"w\":15,\"x\":33,\"y\":68},\"panelIndex\":\"34\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_34\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"36\",\"w\":9,\"x\":0,\"y\":55},\"panelIndex\":\"36\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Creation Summary [Windows System Security]\",\"panelRefName\":\"panel_36\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"37\",\"w\":9,\"x\":9,\"y\":55},\"panelIndex\":\"37\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Changes Summary [Windows System Security]\",\"panelRefName\":\"panel_37\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":13,\"i\":\"38\",\"w\":9,\"x\":18,\"y\":55},\"panelIndex\":\"38\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Deletion Summary [Windows System Security]\",\"panelRefName\":\"panel_38\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":14,\"i\":\"39\",\"w\":16,\"x\":0,\"y\":75},\"panelIndex\":\"39\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Users Added to Group Summary [Windows System Security]\",\"panelRefName\":\"panel_39\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":14,\"i\":\"40\",\"w\":17,\"x\":16,\"y\":75},\"panelIndex\":\"40\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Users Removed From Group Summary [Windows System Security]\",\"panelRefName\":\"panel_40\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":14,\"i\":\"42\",\"w\":15,\"x\":33,\"y\":75},\"panelIndex\":\"42\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Enumeration - Table [Windows System Security]\",\"panelRefName\":\"panel_42\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":20,\"i\":\"43\",\"w\":21,\"x\":27,\"y\":48},\"panelIndex\":\"43\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Details [Windows System Security]\",\"panelRefName\":\"panel_43\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":22,\"i\":\"45\",\"w\":48,\"x\":0,\"y\":89},\"panelIndex\":\"45\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Management Operations Details [Windows System Security]\",\"panelRefName\":\"panel_45\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":20,\"i\":\"3f7e277d-09d1-4a79-bc17-bc5da5a7e290\",\"w\":20,\"x\":0,\"y\":7},\"panelIndex\":\"3f7e277d-09d1-4a79-bc17-bc5da5a7e290\",\"embeddableConfig\":{\"colors\":{\"added-group-account\":\"#0A437C\",\"added-member-to-group\":\"#1F78C1\",\"deleted-group-account\":\"#5195CE\",\"modified-group-account\":\"#052B51\",\"user-member-enumerated\":\"#447EBC\"},\"vis\":{\"colors\":{\"added-group-account\":\"#0A437C\",\"added-member-to-group\":\"#1F78C1\",\"deleted-group-account\":\"#82B5D8\",\"modified-group-account\":\"#052B51\",\"user-member-enumerated\":\"#447EBC\"}},\"enhancements\":{}},\"panelRefName\":\"panel_3f7e277d-09d1-4a79-bc17-bc5da5a7e290\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":20,\"i\":\"8cda9d6a-096f-41a5-86e6-09dd1f6b9c98\",\"w\":16,\"x\":32,\"y\":7},\"panelIndex\":\"8cda9d6a-096f-41a5-86e6-09dd1f6b9c98\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8cda9d6a-096f-41a5-86e6-09dd1f6b9c98\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":20,\"i\":\"74edddd5-2dc5-41b8-b4f2-bf9c95218f1b\",\"w\":12,\"x\":20,\"y\":7},\"panelIndex\":\"74edddd5-2dc5-41b8-b4f2-bf9c95218f1b\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Group Management Events - Event Actions - Table [Windows System Security]\",\"panelRefName\":\"panel_74edddd5-2dc5-41b8-b4f2-bf9c95218f1b\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"33cef054-615a-49cb-bb2e-eb55fab96ae5\",\"w\":27,\"x\":0,\"y\":27},\"panelIndex\":\"33cef054-615a-49cb-bb2e-eb55fab96ae5\",\"embeddableConfig\":{\"vis\":null,\"enhancements\":{}},\"panelRefName\":\"panel_33cef054-615a-49cb-bb2e-eb55fab96ae5\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"e0d495aa-f897-403f-815b-6116fae330b7\",\"w\":21,\"x\":27,\"y\":27},\"panelIndex\":\"e0d495aa-f897-403f-815b-6116fae330b7\",\"embeddableConfig\":{\"colors\":{\"added-group-account\":\"#1F78C1\",\"added-member-to-group\":\"#0A437C\",\"deleted-group-account\":\"#5195CE\",\"modified-group-account\":\"#0A50A1\",\"type-changed-group-account\":\"#82B5D8\",\"user-member-enumerated\":\"#447EBC\"},\"vis\":{\"colors\":{\"added-group-account\":\"#1F78C1\",\"added-member-to-group\":\"#0A437C\",\"deleted-group-account\":\"#5195CE\",\"modified-group-account\":\"#0A50A1\",\"removed-member-from-group\":\"#BADFF4\",\"type-changed-group-account\":\"#82B5D8\",\"user-member-enumerated\":\"#447EBC\"}},\"enhancements\":{}},\"panelRefName\":\"panel_e0d495aa-f897-403f-815b-6116fae330b7\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"663e0493-2070-407b-9d00-079915cce7e7\",\"w\":32,\"x\":16,\"y\":0},\"panelIndex\":\"663e0493-2070-407b-9d00-079915cce7e7\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_663e0493-2070-407b-9d00-079915cce7e7\"}]", + "timeRestore": false, + "title": "[System Windows Security] Group Management Events", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-bb858830-f412-11e9-8405-516218e3d268", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-6f0f2ea0-f414-11e9-8405-516218e3d268", + "name": "22:panel_22", + "type": "visualization" + }, + { + "id": "system-ffebe440-f419-11e9-8405-516218e3d268", + "name": "29:panel_29", + "type": "visualization" + }, + { + "id": "system-e22c6f40-f498-11e9-8405-516218e3d268", + "name": "30:panel_30", + "type": "visualization" + }, + { + "id": "system-ee292bc0-f499-11e9-8405-516218e3d268", + "name": "31:panel_31", + "type": "visualization" + }, + { + "id": "system-400b63e0-f49a-11e9-8405-516218e3d268", + "name": "32:panel_32", + "type": "visualization" + }, + { + "id": "system-a5f664c0-f49a-11e9-8405-516218e3d268", + "name": "33:panel_33", + "type": "visualization" + }, + { + "id": "system-546febc0-f49b-11e9-8405-516218e3d268", + "name": "34:panel_34", + "type": "visualization" + }, + { + "id": "system-98884120-f49d-11e9-8405-516218e3d268", + "name": "36:panel_36", + "type": "visualization" + }, + { + "id": "system-9e534190-f49d-11e9-8405-516218e3d268", + "name": "37:panel_37", + "type": "visualization" + }, + { + "id": "system-bb9cf7a0-f49d-11e9-8405-516218e3d268", + "name": "38:panel_38", + "type": "visualization" + }, + { + "id": "system-ce867840-f49e-11e9-8405-516218e3d268", + "name": "39:panel_39", + "type": "visualization" + }, + { + "id": "system-fee83900-f49f-11e9-8405-516218e3d268", + "name": "40:panel_40", + "type": "visualization" + }, + { + "id": "system-bc165210-f4b8-11e9-8405-516218e3d268", + "name": "42:panel_42", + "type": "visualization" + }, + { + "id": "system-7e178c80-fee1-11e9-8405-516218e3d268", + "name": "43:panel_43", + "type": "search" + }, + { + "id": "system-9066d5b0-fef2-11e9-8405-516218e3d268", + "name": "45:panel_45", + "type": "search" + }, + { + "id": "system-b89b0c90-9b41-11ea-87e4-49f31ec44891", + "name": "3f7e277d-09d1-4a79-bc17-bc5da5a7e290:panel_3f7e277d-09d1-4a79-bc17-bc5da5a7e290", + "type": "visualization" + }, + { + "id": "system-58fb9480-9b46-11ea-87e4-49f31ec44891", + "name": "8cda9d6a-096f-41a5-86e6-09dd1f6b9c98:panel_8cda9d6a-096f-41a5-86e6-09dd1f6b9c98", + "type": "visualization" + }, + { + "id": "system-33462600-9b47-11ea-87e4-49f31ec44891", + "name": "74edddd5-2dc5-41b8-b4f2-bf9c95218f1b:panel_74edddd5-2dc5-41b8-b4f2-bf9c95218f1b", + "type": "visualization" + }, + { + "id": "system-e20c02d0-9b48-11ea-87e4-49f31ec44891", + "name": "33cef054-615a-49cb-bb2e-eb55fab96ae5:panel_33cef054-615a-49cb-bb2e-eb55fab96ae5", + "type": "visualization" + }, + { + "id": "system-7de2e3f0-9b4d-11ea-87e4-49f31ec44891", + "name": "e0d495aa-f897-403f-815b-6116fae330b7:panel_e0d495aa-f897-403f-815b-6116fae330b7", + "type": "visualization" + }, + { + "id": "system-a3c3f350-9b6d-11ea-87e4-49f31ec44891", + "name": "663e0493-2070-407b-9d00-079915cce7e7:panel_663e0493-2070-407b-9d00-079915cce7e7", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzM4LDFd" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "title": "Failed Logon and Account Lockout [Windows System Security]", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[],\"params\":{\"fontSize\":10,\"markdown\":\"### **Failed Logons and Account Lockouts**\",\"openLinksInNewTab\":false},\"title\":\"Failed Logon and Account Lockout [Windows System Security]\",\"type\":\"markdown\"}" + }, + "coreMigrationVersion": "8.1.0", + "id": "system-c2ea73f0-a4bd-11e9-a422-d144027429da", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzE0OCwxXQ==" +} + +{ + "attributes": { + "description": "Failed and blocked accounts with TSVB metrics.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"1\",\"w\":14,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_1\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":18,\"i\":\"2\",\"w\":12,\"x\":0,\"y\":7},\"panelIndex\":\"2\",\"embeddableConfig\":{\"colors\":{\"Failed Logins\":\"#EF843C\",\"Failed Logons\":\"#E24D42\",\"Successful Login\":\"#B7DBAB\",\"Successful Logon\":\"#9AC48A\"},\"legendOpen\":true,\"vis\":{\"colors\":{\"Failed Logins\":\"#EF843C\",\"Failed Logons\":\"#BF1B00\",\"Successful Login\":\"#B7DBAB\",\"Successful Logon\":\"#9AC48A\"},\"legendOpen\":true},\"enhancements\":{}},\"title\":\"Login Successful vs Failed\",\"panelRefName\":\"panel_2\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"3\",\"w\":11,\"x\":12,\"y\":35},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Blocked Acoounts\",\"panelRefName\":\"panel_3\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":18,\"i\":\"4\",\"w\":23,\"x\":12,\"y\":7},\"panelIndex\":\"4\",\"embeddableConfig\":{\"colors\":{\"Login Failed\":\"#F9934E\",\"Login OK\":\"#9AC48A\",\"Logon Failed\":\"#E24D42\",\"Logon Successful\":\"#9AC48A\"},\"legendOpen\":true,\"vis\":{\"colors\":{\"Login Failed\":\"#F9934E\",\"Login OK\":\"#9AC48A\",\"Logon Failed\":\"#BF1B00\",\"Logon Successful\":\"#9AC48A\"},\"legendOpen\":true},\"enhancements\":{}},\"title\":\"Logon Successful and Failed Over time\",\"panelRefName\":\"panel_4\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"5\",\"w\":12,\"x\":0,\"y\":35},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":30,\"i\":\"6\",\"w\":48,\"x\":0,\"y\":56},\"panelIndex\":\"6\",\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 5\":\"rgb(255,245,240)\",\"10 - 15\":\"rgb(252,138,106)\",\"15 - 20\":\"rgb(241,68,50)\",\"20 - 24\":\"rgb(188,20,26)\",\"5 - 10\":\"rgb(253,202,181)\"},\"legendOpen\":false},\"enhancements\":{}},\"title\":\"Logon Failed (Time Mosaic View)\",\"panelRefName\":\"panel_6\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":20,\"i\":\"8\",\"w\":48,\"x\":0,\"y\":86},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Failed and Account Lockouts\",\"panelRefName\":\"panel_8\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":18,\"i\":\"10\",\"w\":13,\"x\":35,\"y\":7},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Failed Source IPs\",\"panelRefName\":\"panel_10\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":31,\"i\":\"11\",\"w\":25,\"x\":23,\"y\":25},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Failed Logins Table\",\"panelRefName\":\"panel_11\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"628de26f-7b7b-457c-b811-e06161e4e7b4\",\"w\":34,\"x\":14,\"y\":0},\"panelIndex\":\"628de26f-7b7b-457c-b811-e06161e4e7b4\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_628de26f-7b7b-457c-b811-e06161e4e7b4\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"01a624c2-7a86-4fa9-89d3-e2ae84e94ec9\",\"w\":12,\"x\":0,\"y\":25},\"panelIndex\":\"01a624c2-7a86-4fa9-89d3-e2ae84e94ec9\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_01a624c2-7a86-4fa9-89d3-e2ae84e94ec9\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"e3046900-1ffc-4efa-9dab-613d685c617b\",\"w\":11,\"x\":12,\"y\":25},\"panelIndex\":\"e3046900-1ffc-4efa-9dab-613d685c617b\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_e3046900-1ffc-4efa-9dab-613d685c617b\"}]", + "timeRestore": false, + "title": "[System Windows Security] Failed and Blocked Accounts", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-d401ef40-a7d5-11e9-a422-d144027429da", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-c2ea73f0-a4bd-11e9-a422-d144027429da", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-175a5760-a7d5-11e9-a422-d144027429da", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "system-7a329a00-a7d5-11e9-a422-d144027429da", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-162d7ab0-a7d6-11e9-a422-d144027429da", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "system-729443b0-a7d6-11e9-a422-d144027429da", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "system-4b683ac0-a7d7-11e9-a422-d144027429da", + "name": "6:panel_6", + "type": "visualization" + }, + { + "id": "system-757510b0-a87f-11e9-a422-d144027429da", + "name": "8:panel_8", + "type": "search" + }, + { + "id": "system-2084e300-a884-11e9-a422-d144027429da", + "name": "10:panel_10", + "type": "visualization" + }, + { + "id": "system-421f0610-af98-11e9-a422-d144027429da", + "name": "11:panel_11", + "type": "visualization" + }, + { + "id": "system-a3c3f350-9b6d-11ea-87e4-49f31ec44891", + "name": "628de26f-7b7b-457c-b811-e06161e4e7b4:panel_628de26f-7b7b-457c-b811-e06161e4e7b4", + "type": "visualization" + }, + { + "id": "system-8ef59f90-6ab8-11ea-896f-0d70f7ec3956", + "name": "01a624c2-7a86-4fa9-89d3-e2ae84e94ec9:panel_01a624c2-7a86-4fa9-89d3-e2ae84e94ec9", + "type": "visualization" + }, + { + "id": "system-a79395f0-6aba-11ea-896f-0d70f7ec3956", + "name": "e3046900-1ffc-4efa-9dab-613d685c617b:panel_e3046900-1ffc-4efa-9dab-613d685c617b", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzM5LDFd" +} + +{ + "attributes": { + "description": "Failed and blocked accounts.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"kuery\",\"query\":\"data_stream.dataset:windows.security OR data_stream.dataset:system.security\"}}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":false}", + "panelsJSON": "[{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"1\",\"w\":14,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_1\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":18,\"i\":\"2\",\"w\":12,\"x\":0,\"y\":7},\"panelIndex\":\"2\",\"embeddableConfig\":{\"colors\":{\"Failed Logins\":\"#EF843C\",\"Failed Logons\":\"#E24D42\",\"Successful Login\":\"#B7DBAB\",\"Successful Logon\":\"#9AC48A\"},\"legendOpen\":true,\"vis\":{\"colors\":{\"Failed Logins\":\"#EF843C\",\"Failed Logons\":\"#BF1B00\",\"Successful Login\":\"#B7DBAB\",\"Successful Logon\":\"#9AC48A\"},\"legendOpen\":true},\"enhancements\":{}},\"title\":\"Login Successful vs Failed\",\"panelRefName\":\"panel_2\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"3\",\"w\":11,\"x\":12,\"y\":35},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Blocked Acoounts\",\"panelRefName\":\"panel_3\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":18,\"i\":\"4\",\"w\":23,\"x\":12,\"y\":7},\"panelIndex\":\"4\",\"embeddableConfig\":{\"colors\":{\"Login Failed\":\"#F9934E\",\"Login OK\":\"#9AC48A\",\"Logon Failed\":\"#E24D42\",\"Logon Successful\":\"#9AC48A\"},\"legendOpen\":true,\"vis\":{\"colors\":{\"Login Failed\":\"#F9934E\",\"Login OK\":\"#9AC48A\",\"Logon Failed\":\"#BF1B00\",\"Logon Successful\":\"#9AC48A\"},\"legendOpen\":true},\"enhancements\":{}},\"title\":\"Logon Successful and Failed Over time\",\"panelRefName\":\"panel_4\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":21,\"i\":\"5\",\"w\":12,\"x\":0,\"y\":35},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":30,\"i\":\"6\",\"w\":48,\"x\":0,\"y\":56},\"panelIndex\":\"6\",\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 5\":\"rgb(255,245,240)\",\"10 - 15\":\"rgb(252,138,106)\",\"15 - 20\":\"rgb(241,68,50)\",\"20 - 24\":\"rgb(188,20,26)\",\"5 - 10\":\"rgb(253,202,181)\"},\"legendOpen\":false},\"enhancements\":{}},\"title\":\"Logon Failed (Time Mosaic View)\",\"panelRefName\":\"panel_6\"},{\"version\":\"7.7.0\",\"type\":\"search\",\"gridData\":{\"h\":20,\"i\":\"8\",\"w\":48,\"x\":0,\"y\":86},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Failed and Account Lockouts\",\"panelRefName\":\"panel_8\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":18,\"i\":\"10\",\"w\":13,\"x\":35,\"y\":7},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Logon Failed Source IPs\",\"panelRefName\":\"panel_10\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":31,\"i\":\"11\",\"w\":25,\"x\":23,\"y\":25},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"Failed Logins Table\",\"panelRefName\":\"panel_11\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":7,\"i\":\"a79ee89f-ff45-486c-9788-9446d39456c2\",\"w\":34,\"x\":14,\"y\":0},\"panelIndex\":\"a79ee89f-ff45-486c-9788-9446d39456c2\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_a79ee89f-ff45-486c-9788-9446d39456c2\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"7765df59-11c4-476d-898f-9ebf98c369e2\",\"w\":11,\"x\":12,\"y\":25},\"panelIndex\":\"7765df59-11c4-476d-898f-9ebf98c369e2\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_7765df59-11c4-476d-898f-9ebf98c369e2\"},{\"version\":\"7.7.0\",\"type\":\"visualization\",\"gridData\":{\"h\":10,\"i\":\"b47c91d3-58c4-4b5b-b302-444b048efdfa\",\"w\":12,\"x\":0,\"y\":25},\"panelIndex\":\"b47c91d3-58c4-4b5b-b302-444b048efdfa\",\"embeddableConfig\":{\"enhancements\":{}},\"title\":\"\",\"panelRefName\":\"panel_b47c91d3-58c4-4b5b-b302-444b048efdfa\"}]", + "timeRestore": false, + "title": "[System Windows Security] Failed and Blocked Accounts - Simple Metrics", + "version": 1 + }, + "coreMigrationVersion": "8.1.0", + "id": "system-f49f3170-9ffc-11ea-87e4-49f31ec44891", + "migrationVersion": { + "dashboard": "8.1.0" + }, + "references": [ + { + "id": "system-c2ea73f0-a4bd-11e9-a422-d144027429da", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "system-175a5760-a7d5-11e9-a422-d144027429da", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "system-7a329a00-a7d5-11e9-a422-d144027429da", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "system-162d7ab0-a7d6-11e9-a422-d144027429da", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "system-729443b0-a7d6-11e9-a422-d144027429da", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "system-4b683ac0-a7d7-11e9-a422-d144027429da", + "name": "6:panel_6", + "type": "visualization" + }, + { + "id": "system-757510b0-a87f-11e9-a422-d144027429da", + "name": "8:panel_8", + "type": "search" + }, + { + "id": "system-2084e300-a884-11e9-a422-d144027429da", + "name": "10:panel_10", + "type": "visualization" + }, + { + "id": "system-421f0610-af98-11e9-a422-d144027429da", + "name": "11:panel_11", + "type": "visualization" + }, + { + "id": "system-d770b040-9b35-11ea-87e4-49f31ec44891", + "name": "a79ee89f-ff45-486c-9788-9446d39456c2:panel_a79ee89f-ff45-486c-9788-9446d39456c2", + "type": "visualization" + }, + { + "id": "system-5d117970-9ffd-11ea-87e4-49f31ec44891", + "name": "7765df59-11c4-476d-898f-9ebf98c369e2:panel_7765df59-11c4-476d-898f-9ebf98c369e2", + "type": "visualization" + }, + { + "id": "system-4bedf650-9ffd-11ea-87e4-49f31ec44891", + "name": "b47c91d3-58c4-4b5b-b302-444b048efdfa:panel_b47c91d3-58c4-4b5b-b302-444b048efdfa", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2022-01-20T15:41:14.393Z", + "version": "WzQwLDFd" +} \ No newline at end of file diff --git a/x-pack/test/performance/page_objects.ts b/x-pack/test/performance/page_objects.ts index 4744980b82592..6c273213bf4a1 100644 --- a/x-pack/test/performance/page_objects.ts +++ b/x-pack/test/performance/page_objects.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from '../functional/page_objects'; +export const pageObjects = {}; diff --git a/x-pack/test/performance/services.ts b/x-pack/test/performance/services.ts deleted file mode 100644 index ecaac6362761d..0000000000000 --- a/x-pack/test/performance/services.ts +++ /dev/null @@ -1,8 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from '../functional/services'; diff --git a/x-pack/test/performance/services/index.ts b/x-pack/test/performance/services/index.ts new file mode 100644 index 0000000000000..99cc10bc14bdc --- /dev/null +++ b/x-pack/test/performance/services/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { services as functionalServices } from '../../functional/services'; +import { PerformanceTestingService } from './performance'; +import { InputDelaysProvider } from './input_delays'; + +export const services = { + es: functionalServices.es, + kibanaServer: functionalServices.kibanaServer, + esArchiver: functionalServices.esArchiver, + retry: functionalServices.retry, + performance: PerformanceTestingService, + inputDelays: InputDelaysProvider, +}; diff --git a/x-pack/test/performance/services/input_delays.ts b/x-pack/test/performance/services/input_delays.ts new file mode 100644 index 0000000000000..483974cb9802d --- /dev/null +++ b/x-pack/test/performance/services/input_delays.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +interface InputDelays { + TYPING: number; + MOUSE_CLICK: number; +} + +const PROFILES: Record = { + user: { + TYPING: 500, + MOUSE_CLICK: 1000, + }, + asap: { + TYPING: 5, + MOUSE_CLICK: 5, + }, +}; + +export function InputDelaysProvider(): InputDelays { + const profile = PROFILES[process.env.INPUT_DELAY_PROFILE ?? 'user']; + + if (!profile) { + throw new Error( + `invalid INPUT_DELAY_PROFILE value, expected one of (${Object.keys(PROFILES).join(', ')})` + ); + } + + return profile; +} diff --git a/x-pack/test/performance/services/performance.ts b/x-pack/test/performance/services/performance.ts new file mode 100644 index 0000000000000..3905a61bf7b33 --- /dev/null +++ b/x-pack/test/performance/services/performance.ts @@ -0,0 +1,248 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable no-console */ + +import Url from 'url'; +import { inspect } from 'util'; +import apm, { Span, Transaction } from 'elastic-apm-node'; +import { setTimeout } from 'timers/promises'; +import playwright, { ChromiumBrowser, Page, BrowserContext } from 'playwright'; +import { FtrService, FtrProviderContext } from '../ftr_provider_context'; + +type StorageState = Awaited>; + +apm.start({ + secretToken: 'Q5q5rWQEw6tKeirBpw', + serverUrl: 'https://2fad4006bf784bb8a54e52f4a5862609.apm.us-west1.gcp.cloud.es.io:443', + serviceName: 'functional test runner', +}); + +interface StepCtx { + page: Page; +} +type StepFn = (ctx: StepCtx) => Promise; +type Steps = Array<{ name: string; fn: StepFn }>; + +export class PerformanceTestingService extends FtrService { + private readonly config = this.ctx.getService('config'); + private readonly lifecycle = this.ctx.getService('lifecycle'); + private readonly inputDelays = this.ctx.getService('inputDelays'); + private browser: ChromiumBrowser | undefined; + private storageState: StorageState | undefined; + private currentSpanStack: Array = []; + private currentTransaction: Transaction | undefined | null; + + constructor(ctx: FtrProviderContext) { + super(ctx); + + this.lifecycle.beforeTests.add(async () => { + await this.withTransaction('Journey setup', async () => { + await this.getStorageState(); + }); + }); + + this.lifecycle.cleanup.add(async () => { + apm.flush(); + await setTimeout(5000); + await this.browser?.close(); + }); + } + + private async withTransaction(name: string, block: () => Promise) { + try { + if (this.currentTransaction !== undefined) { + throw new Error( + `Transaction already started, make sure you end transaction ${this.currentTransaction?.name}` + ); + } + this.currentTransaction = apm.startTransaction(name, 'performance'); + const result = await block(); + if (this.currentTransaction === undefined) { + throw new Error(`No transaction started`); + } + this.currentTransaction?.end('success'); + this.currentTransaction = undefined; + return result; + } catch (e) { + if (this.currentTransaction === undefined) { + throw new Error(`No transaction started`); + } + this.currentTransaction?.end('failure'); + this.currentTransaction = undefined; + throw e; + } + } + + private async withSpan(name: string, type: string | undefined, block: () => Promise) { + try { + this.currentSpanStack.unshift(apm.startSpan(name, type ?? null)); + const result = await block(); + if (this.currentSpanStack.length === 0) { + throw new Error(`No Span started`); + } + const span = this.currentSpanStack.shift(); + span?.setOutcome('success'); + span?.end(); + return result; + } catch (e) { + if (this.currentSpanStack.length === 0) { + throw new Error(`No Span started`); + } + const span = this.currentSpanStack.shift(); + span?.setOutcome('failure'); + span?.end(); + throw e; + } + } + + private getCurrentTraceparent() { + return (this.currentSpanStack.length ? this.currentSpanStack[0] : this.currentTransaction) + ?.traceparent; + } + + private async getStorageState() { + if (this.storageState) { + return this.storageState; + } + + await this.withSpan('initial login', undefined, async () => { + const kibanaUrl = Url.format({ + protocol: this.config.get('servers.kibana.protocol'), + hostname: this.config.get('servers.kibana.hostname'), + port: this.config.get('servers.kibana.port'), + }); + + const browser = await this.getBrowserInstance(); + const context = await browser.newContext(); + const page = await context.newPage(); + await this.interceptBrowserRequests(page); + await page.goto(`${kibanaUrl}`); + + const usernameLocator = page.locator('[data-test-subj=loginUsername]'); + const passwordLocator = page.locator('[data-test-subj=loginPassword]'); + const submitButtonLocator = page.locator('[data-test-subj=loginSubmit]'); + + await usernameLocator?.type('elastic', { delay: this.inputDelays.TYPING }); + await passwordLocator?.type('changeme', { delay: this.inputDelays.TYPING }); + await submitButtonLocator?.click({ delay: this.inputDelays.MOUSE_CLICK }); + + await page.waitForSelector('#headerUserMenu'); + + this.storageState = await page.context().storageState(); + await page.close(); + await context.close(); + }); + + return this.storageState; + } + + private async getBrowserInstance() { + if (this.browser) { + return this.browser; + } + return await this.withSpan('browser creation', 'setup', async () => { + const headless = !!(process.env.TEST_BROWSER_HEADLESS || process.env.CI); + this.browser = await playwright.chromium.launch({ headless, timeout: 60000 }); + return this.browser; + }); + } + + private async sendCDPCommands(context: BrowserContext, page: Page) { + const client = await context.newCDPSession(page); + + await client.send('Network.clearBrowserCache'); + await client.send('Network.setCacheDisabled', { cacheDisabled: true }); + await client.send('Network.emulateNetworkConditions', { + latency: 100, + downloadThroughput: 750_000, + uploadThroughput: 750_000, + offline: false, + }); + + return client; + } + + private async interceptBrowserRequests(page: Page) { + await page.route('**', async (route, request) => { + const headers = await request.allHeaders(); + const traceparent = this.getCurrentTraceparent(); + if (traceparent && request.isNavigationRequest()) { + await route.continue({ headers: { traceparent, ...headers } }); + } else { + await route.continue(); + } + }); + } + + public makePage(journeyName: string) { + const steps: Steps = []; + + it(journeyName, async () => { + await this.withTransaction(`Journey ${journeyName}`, async () => { + const browser = await this.getBrowserInstance(); + const context = await browser.newContext({ + viewport: { width: 1600, height: 1200 }, + storageState: await this.getStorageState(), + }); + + const page = await context.newPage(); + page.on('console', (message) => { + (async () => { + try { + const args = await Promise.all( + message.args().map(async (handle) => handle.jsonValue()) + ); + + const { url, lineNumber, columnNumber } = message.location(); + + const location = `${url},${lineNumber},${columnNumber}`; + + const text = args.length + ? args.map((arg) => (typeof arg === 'string' ? arg : inspect(arg))).join(' ') + : message.text(); + + console.log(`[console.${message.type()}]`, text); + console.log(' ', location); + } catch (e) { + console.error('Failed to evaluate console.log line', e); + } + })(); + }); + const client = await this.sendCDPCommands(context, page); + + await this.interceptBrowserRequests(page); + + try { + for (const step of steps) { + await this.withSpan(`step: ${step.name}`, 'step', async () => { + try { + await step.fn({ page }); + } catch (e) { + const error = new Error(`Step [${step.name}] failed: ${e.message}`); + error.stack = e.stack; + throw error; + } + }); + } + } finally { + if (page) { + await client.detach(); + await page.close(); + await context.close(); + } + } + }); + }); + + return { + step: (name: string, fn: StepFn) => { + steps.push({ name, fn }); + }, + }; + } +} diff --git a/x-pack/test/performance/tests/ftr/reporting_dashboard.ts b/x-pack/test/performance/tests/ftr/reporting_dashboard.ts deleted file mode 100644 index 6a696dda5afb9..0000000000000 --- a/x-pack/test/performance/tests/ftr/reporting_dashboard.ts +++ /dev/null @@ -1,53 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService, getPageObject }: FtrProviderContext) { - const retry = getService('retry'); - const es = getService('es'); - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - const common = getPageObject('common'); - const dashboard = getPageObject('dashboard'); - const reporting = getPageObject('reporting'); - - describe('Reporting Dashboard', () => { - before(async () => { - await kibanaServer.importExport.load( - 'x-pack/test/performance/kbn_archives/reporting_dashboard' - ); - await esArchiver.loadIfNeeded('x-pack/test/performance/es_archives/reporting_dashboard'); - }); - - after(async () => { - await kibanaServer.importExport.unload( - 'x-pack/test/performance/kbn_archives/reporting_dashboard' - ); - await esArchiver.unload('x-pack/test/performance/es_archives/reporting_dashboard'); - await es.deleteByQuery({ - index: '.reporting-*', - refresh: true, - body: { query: { match_all: {} } }, - }); - }); - - it('downloaded PDF has OK status', async function () { - this.timeout(180000); - - await common.navigateToApp('dashboards'); - await retry.waitFor('dashboard landing page', async () => { - return await dashboard.onDashboardLandingPage(); - }); - await dashboard.loadSavedDashboard('dashboard'); - await reporting.openPdfReportingPanel(); - await reporting.clickGenerateReportButton(); - - await reporting.getReportURL(60000); - }); - }); -} diff --git a/x-pack/test/performance/tests/playwright/ecommerce_dashboard.ts b/x-pack/test/performance/tests/playwright/ecommerce_dashboard.ts new file mode 100644 index 0000000000000..1f75d39a3cd62 --- /dev/null +++ b/x-pack/test/performance/tests/playwright/ecommerce_dashboard.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import Url from 'url'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ecommerceDashboard({ getService }: FtrProviderContext) { + describe('ecommerce_dashboard', () => { + const config = getService('config'); + const performance = getService('performance'); + const logger = getService('log'); + + const { step } = performance.makePage('ecommerce_dashboard'); + + step('Go to Sample Data Page', async ({ page }) => { + const kibanaUrl = Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }); + + await page.goto(`${kibanaUrl}/app/home#/tutorial_directory/sampleData`); + await page.waitForSelector('text="More ways to add data"'); + }); + + step('Add Ecommerce Sample Data', async ({ page }) => { + const removeButton = page.locator('[data-test-subj=removeSampleDataSetecommerce]'); + try { + await removeButton.click({ timeout: 1_000 }); + } catch (e) { + logger.info('Ecommerce data does not exist'); + } + const addDataButton = page.locator('[data-test-subj=addSampleDataSetecommerce]'); + if (addDataButton) { + await addDataButton.click(); + } + }); + + step('Go to Ecommerce Dashboard', async ({ page }) => { + await page.click('[data-test-subj=launchSampleDataSetecommerce]'); + await page.click('[data-test-subj=viewSampleDataSetecommerce-dashboard]'); + + await page.waitForFunction(() => { + const visualizations = Array.from(document.querySelectorAll('[data-rendering-count]')); + const visualizationElementsLoaded = visualizations.length > 0; + const visualizationAnimationsFinished = visualizations.every( + (e) => e.getAttribute('data-render-complete') === 'true' + ); + return visualizationElementsLoaded && visualizationAnimationsFinished; + }); + }); + }); +} diff --git a/x-pack/test/performance/tests/playwright/flight_dashboard.ts b/x-pack/test/performance/tests/playwright/flight_dashboard.ts new file mode 100644 index 0000000000000..04ea397d95e34 --- /dev/null +++ b/x-pack/test/performance/tests/playwright/flight_dashboard.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import Url from 'url'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function flightDashboard({ getService }: FtrProviderContext) { + describe('flights_dashboard', () => { + const config = getService('config'); + const performance = getService('performance'); + const logger = getService('log'); + const { step } = performance.makePage('flights_dashboard'); + + step('Go to Sample Data Page', async ({ page }) => { + const kibanaUrl = Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }); + + await page.goto(`${kibanaUrl}/app/home#/tutorial_directory/sampleData`); + await page.waitForSelector('[data-test-subj=sampleDataSetCardflights]'); + }); + + step('Add Flights Sample Data', async ({ page }) => { + const removeButton = page.locator('[data-test-subj=removeSampleDataSetflights]'); + try { + await removeButton.click({ timeout: 1_000 }); + } catch (e) { + logger.info('Flights data does not exist'); + } + + const addDataButton = page.locator('[data-test-subj=addSampleDataSetflights]'); + if (addDataButton) { + await addDataButton.click(); + } + }); + + step('Go to Flights Dashboard', async ({ page }) => { + await page.click('[data-test-subj=launchSampleDataSetflights]'); + await page.click('[data-test-subj=viewSampleDataSetflights-dashboard]'); + + await page.waitForFunction(() => { + const visualizations = Array.from(document.querySelectorAll('[data-rendering-count]')); + const visualizationElementsLoaded = visualizations.length > 0; + const visualizationAnimationsFinished = visualizations.every( + (e) => e.getAttribute('data-render-complete') === 'true' + ); + return visualizationElementsLoaded && visualizationAnimationsFinished; + }); + }); + // embeddablePanelHeading-[Flights]AirportConnections(HoverOverAirport) + step('Go to Airport Connections Visualizations Edit', async ({ page }) => { + await page.click('[data-test-subj="dashboardEditMode"]'); + + const flightsPanelHeadingSelector = `[data-test-subj="embeddablePanelHeading-[Flights]AirportConnections(HoverOverAirport)"]`; + const panelToggleMenuIconSelector = `[data-test-subj="embeddablePanelToggleMenuIcon"]`; + + await page.click(`${flightsPanelHeadingSelector} ${panelToggleMenuIconSelector}`); + + await page.click('[data-test-subj="embeddablePanelAction-editPanel"]'); + + await page.waitForFunction(() => { + const visualization = document.querySelector('[data-rendering-count]'); + return visualization && visualization?.getAttribute('data-render-complete') === 'true'; + }); + }); + }); +} diff --git a/x-pack/test/performance/tests/playwright/home.ts b/x-pack/test/performance/tests/playwright/home.ts deleted file mode 100644 index 2460464c80987..0000000000000 --- a/x-pack/test/performance/tests/playwright/home.ts +++ /dev/null @@ -1,55 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import Url from 'url'; -import { ChromiumBrowser, Page } from 'playwright'; -import testSetup from './setup'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - describe('perf_login_and_home', () => { - const config = getService('config'); - const kibanaUrl = Url.format({ - protocol: config.get('servers.kibana.protocol'), - hostname: config.get('servers.kibana.hostname'), - port: config.get('servers.kibana.port'), - }); - - let page: Page | null = null; - let browser: ChromiumBrowser | null = null; - - before(async () => { - const context = await testSetup(); - page = context.page; - browser = context.browser; - }); - - after(async () => { - await browser?.close(); - }); - - it('Go to Kibana login page', async () => { - await page?.goto(`${kibanaUrl}`); - }); - - it('Login to Kibana', async () => { - const usernameLocator = page?.locator('[data-test-subj=loginUsername]'); - const passwordLocator = page?.locator('[data-test-subj=loginPassword]'); - const submitButtonLocator = page?.locator('[data-test-subj=loginSubmit]'); - - await usernameLocator?.type('elastic', { delay: 500 }); - await passwordLocator?.type('changeme', { delay: 500 }); - await submitButtonLocator?.click({ delay: 1000 }); - }); - - it('Dismiss Welcome Screen', async () => { - await page?.waitForLoadState(); - const skipButtonLocator = page?.locator('[data-test-subj=skipWelcomeScreen]'); - await skipButtonLocator?.click({ delay: 1000 }); - await page?.waitForLoadState('networkidle'); - }); - }); -} diff --git a/x-pack/test/performance/tests/playwright/index.ts b/x-pack/test/performance/tests/playwright/index.ts new file mode 100644 index 0000000000000..7f426ee047d3e --- /dev/null +++ b/x-pack/test/performance/tests/playwright/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Performance tests', () => { + loadTestFile(require.resolve('./ecommerce_dashboard')); + loadTestFile(require.resolve('./flight_dashboard')); + loadTestFile(require.resolve('./web_logs_dashboard')); + loadTestFile(require.resolve('./promotion_tracking_dashboard')); + }); +} diff --git a/x-pack/test/performance/tests/playwright/promotion_tracking_dashboard.ts b/x-pack/test/performance/tests/playwright/promotion_tracking_dashboard.ts new file mode 100644 index 0000000000000..4f36378eb1dc3 --- /dev/null +++ b/x-pack/test/performance/tests/playwright/promotion_tracking_dashboard.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import Url from 'url'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function promotionTrackingDashboard({ getService }: FtrProviderContext) { + describe('promotion_tracking_dashboard', () => { + const config = getService('config'); + const performance = getService('performance'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const { step } = performance.makePage('promotion_tracking_dashboard'); + + before(async () => { + await kibanaServer.importExport.load( + 'x-pack/test/performance/kbn_archives/promotion_tracking_dashboard' + ); + await esArchiver.loadIfNeeded('x-pack/test/performance/es_archives/ecommerce_sample_data'); + }); + + after(async () => { + await kibanaServer.importExport.unload( + 'x-pack/test/performance/kbn_archives/promotion_tracking_dashboard' + ); + await esArchiver.unload('x-pack/test/performance/es_archives/ecommerce_sample_data'); + }); + + step('Go to Dashboards Page', async ({ page }) => { + const kibanaUrl = Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }); + + await page.goto(`${kibanaUrl}/app/dashboards`); + await page.waitForSelector('#dashboardListingHeading'); + }); + + step('Go to Promotion Tracking Dashboard', async ({ page }) => { + const promotionDashboardButton = page.locator( + '[data-test-subj="dashboardListingTitleLink-Promotion-Dashboard"]' + ); + await promotionDashboardButton.click(); + }); + + step('Change time range', async ({ page }) => { + const beginningTimeRangeButton = page.locator( + '[data-test-subj="superDatePickerToggleQuickMenuButton"]' + ); + await beginningTimeRangeButton.click(); + + const lastYearButton = page.locator( + '[data-test-subj="superDatePickerCommonlyUsed_Last_30 days"]' + ); + await lastYearButton.click(); + }); + + step('Wait for visualization animations to finish', async ({ page }) => { + await page.waitForFunction(() => { + const visualizations = Array.from(document.querySelectorAll('[data-rendering-count]')); + const visualizationElementsLoaded = visualizations.length > 0; + const visualizationAnimationsFinished = visualizations.every( + (e) => e.getAttribute('data-render-complete') === 'true' + ); + return visualizationElementsLoaded && visualizationAnimationsFinished; + }); + }); + }); +} diff --git a/x-pack/test/performance/tests/playwright/setup.ts b/x-pack/test/performance/tests/playwright/setup.ts deleted file mode 100644 index b89335f19c101..0000000000000 --- a/x-pack/test/performance/tests/playwright/setup.ts +++ /dev/null @@ -1,34 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import playwright, { ChromiumBrowser, Page } from 'playwright'; - -interface ITestSetup { - browser: ChromiumBrowser; - page: Page; -} - -const headless = process.env.TEST_BROWSER_HEADLESS === '1'; - -export default async (): Promise => { - const browser = await playwright.chromium.launch({ headless }); - const page = await browser.newPage(); - const client = await page.context().newCDPSession(page); - - await client.send('Network.clearBrowserCache'); - await client.send('Network.setCacheDisabled', { cacheDisabled: true }); - await client.send('Network.emulateNetworkConditions', { - latency: 100, - downloadThroughput: 750_000, - uploadThroughput: 750_000, - offline: false, - }); - - await page.route('**', (route) => route.continue()); - - return { browser, page }; -}; diff --git a/x-pack/test/performance/tests/playwright/web_logs_dashboard.ts b/x-pack/test/performance/tests/playwright/web_logs_dashboard.ts new file mode 100644 index 0000000000000..6da99a6662c4e --- /dev/null +++ b/x-pack/test/performance/tests/playwright/web_logs_dashboard.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import Url from 'url'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function weblogDashboard({ getService }: FtrProviderContext) { + describe('weblogs_dashboard', () => { + const config = getService('config'); + const performance = getService('performance'); + const logger = getService('log'); + const { step } = performance.makePage('weblogs_dashboard'); + + step('Go to Sample Data Page', async ({ page }) => { + const kibanaUrl = Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }); + + await page.goto(`${kibanaUrl}/app/home#/tutorial_directory/sampleData`); + await page.waitForSelector('text="More ways to add data"'); + }); + + step('Add Web Logs Sample Data', async ({ page }) => { + const removeButton = page.locator('[data-test-subj=removeSampleDataSetlogs]'); + try { + await removeButton.click({ timeout: 1_000 }); + } catch (e) { + logger.info('Weblogs data does not exist'); + } + + const addDataButton = page.locator('[data-test-subj=addSampleDataSetlogs]'); + if (addDataButton) { + await addDataButton.click(); + } + }); + + step('Go to Web Logs Dashboard', async ({ page }) => { + await page.click('[data-test-subj=launchSampleDataSetlogs]'); + await page.click('[data-test-subj=viewSampleDataSetlogs-dashboard]'); + + await page.waitForFunction(() => { + const visualizations = Array.from(document.querySelectorAll('[data-rendering-count]')); + const visualizationElementsLoaded = visualizations.length > 0; + const visualizationAnimationsFinished = visualizations.every( + (e) => e.getAttribute('data-render-complete') === 'true' + ); + return visualizationElementsLoaded && visualizationAnimationsFinished; + }); + }); + }); +} From 7a00d24fd6ea62529ff9d2b6e6ac08a7d27fbb92 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau Date: Tue, 8 Mar 2022 13:59:15 -0500 Subject: [PATCH 074/140] [CORE] Allow find saved object to query on _id (#126933) * allow find saved object to query on _id * rename * more test and some cleaning * review to use id instead of _id to match saved object definition * add more test after talking to Rudolf --- .../service/lib/filter_utils.test.ts | 73 +++++++++++++++++++ .../saved_objects/service/lib/filter_utils.ts | 29 +++++--- 2 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/core/server/saved_objects/service/lib/filter_utils.test.ts b/src/core/server/saved_objects/service/lib/filter_utils.test.ts index e9de6e77f0edf..92eb0d041cc27 100644 --- a/src/core/server/saved_objects/service/lib/filter_utils.test.ts +++ b/src/core/server/saved_objects/service/lib/filter_utils.test.ts @@ -36,6 +36,9 @@ const mockMappings = { }, bar: { properties: { + _id: { + type: 'keyword', + }, foo: { type: 'text', }, @@ -193,6 +196,76 @@ describe('Filter Utils', () => { ).toEqual(esKuery.fromKueryExpression('alert.params.foo:bar')); }); + test('Assemble filter with just "id" and one type', () => { + expect(validateConvertFilterToKueryNode(['foo'], 'foo.id: 0123456789', mockMappings)).toEqual( + esKuery.fromKueryExpression('type: foo and _id: 0123456789') + ); + }); + + test('Assemble filter with saved object attribute "id" and one type and more', () => { + expect( + validateConvertFilterToKueryNode( + ['foo'], + 'foo.id: 0123456789 and (foo.updated_at: 5678654567 or foo.attributes.bytes > 1000)', + mockMappings + ) + ).toEqual( + esKuery.fromKueryExpression( + '(type: foo and _id: 0123456789) and ((type: foo and updated_at: 5678654567) or foo.bytes > 1000)' + ) + ); + }); + + test('Assemble filter with saved object attribute "id" and multi type and more', () => { + expect( + validateConvertFilterToKueryNode( + ['foo', 'bar'], + 'foo.id: 0123456789 and bar.id: 9876543210', + mockMappings + ) + ).toEqual( + esKuery.fromKueryExpression( + '(type: foo and _id: 0123456789) and (type: bar and _id: 9876543210)' + ) + ); + }); + + test('Allow saved object type to defined "_id" attributes and filter on it', () => { + expect( + validateConvertFilterToKueryNode( + ['foo', 'bar'], + 'foo.id: 0123456789 and bar.attributes._id: 9876543210', + mockMappings + ) + ).toEqual( + esKuery.fromKueryExpression('(type: foo and _id: 0123456789) and (bar._id: 9876543210)') + ); + }); + + test('Lets make sure that we are throwing an exception if we are using id outside of saved object attribute when it does not belong', () => { + expect(() => { + validateConvertFilterToKueryNode( + ['foo'], + 'foo.attributes.id: 0123456789 and (foo.updated_at: 5678654567 or foo.attributes.bytes > 1000)', + mockMappings + ); + }).toThrowErrorMatchingInlineSnapshot( + `"This key 'foo.attributes.id' does NOT exist in foo saved object index patterns: Bad Request"` + ); + }); + + test('Lets make sure that we are throwing an exception if we are using _id', () => { + expect(() => { + validateConvertFilterToKueryNode( + ['foo'], + 'foo._id: 0123456789 and (foo.updated_at: 5678654567 or foo.attributes.bytes > 1000)', + mockMappings + ); + }).toThrowErrorMatchingInlineSnapshot( + `"This key 'foo._id' does NOT exist in foo saved object index patterns: Bad Request"` + ); + }); + test('Lets make sure that we are throwing an exception if we get an error', () => { expect(() => { validateConvertFilterToKueryNode( diff --git a/src/core/server/saved_objects/service/lib/filter_utils.ts b/src/core/server/saved_objects/service/lib/filter_utils.ts index 6c8aee832457a..27ff1c201cbdd 100644 --- a/src/core/server/saved_objects/service/lib/filter_utils.ts +++ b/src/core/server/saved_objects/service/lib/filter_utils.ts @@ -22,7 +22,7 @@ export const validateConvertFilterToKueryNode = ( indexMapping: IndexMapping ): KueryNode | undefined => { if (filter && indexMapping) { - const filterKueryNode = + let filterKueryNode = typeof filter === 'string' ? esKuery.fromKueryExpression(filter) : cloneDeep(filter); const validationFilterKuery = validateFilterKueryNode({ @@ -54,17 +54,20 @@ export const validateConvertFilterToKueryNode = ( const existingKueryNode: KueryNode = path.length === 0 ? filterKueryNode : get(filterKueryNode, path); if (item.isSavedObjectAttr) { - existingKueryNode.arguments[0].value = existingKueryNode.arguments[0].value.split('.')[1]; + const keySavedObjectAttr = existingKueryNode.arguments[0].value.split('.')[1]; + existingKueryNode.arguments[0].value = + keySavedObjectAttr === 'id' ? '_id' : keySavedObjectAttr; const itemType = allowedTypes.filter((t) => t === item.type); if (itemType.length === 1) { - set( - filterKueryNode, - path, - esKuery.nodeTypes.function.buildNode('and', [ - esKuery.nodeTypes.function.buildNode('is', 'type', itemType[0]), - existingKueryNode, - ]) - ); + const kueryToAdd = esKuery.nodeTypes.function.buildNode('and', [ + esKuery.nodeTypes.function.buildNode('is', 'type', itemType[0]), + existingKueryNode, + ]); + if (path.length > 0) { + set(filterKueryNode, path, kueryToAdd); + } else { + filterKueryNode = kueryToAdd; + } } } else { existingKueryNode.arguments[0].value = existingKueryNode.arguments[0].value.replace( @@ -171,6 +174,8 @@ export const isSavedObjectAttr = (key: string | null | undefined, indexMapping: const keySplit = key != null ? key.split('.') : []; if (keySplit.length === 1 && fieldDefined(indexMapping, keySplit[0])) { return true; + } else if (keySplit.length === 2 && keySplit[1] === 'id') { + return true; } else if (keySplit.length === 2 && fieldDefined(indexMapping, keySplit[1])) { return true; } else { @@ -219,6 +224,10 @@ export const fieldDefined = (indexMappings: IndexMapping, key: string): boolean return true; } + if (mappingKey === 'properties.id') { + return true; + } + // If the `mappingKey` does not match a valid path, before returning false, // we want to check and see if the intended path was for a multi-field // such as `x.attributes.field.text` where `field` is mapped to both text From 79905bac04589e068813322a8e0bd87c1a8cf367 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 8 Mar 2022 11:29:17 -0800 Subject: [PATCH 075/140] [DOCS] Add Stack Management cases (#126960) --- docs/management/cases/cases.asciidoc | 20 +++++ .../cases/images/cases-visualization.png | Bin 0 -> 159476 bytes docs/management/cases/images/cases.png | Bin 0 -> 82573 bytes docs/management/cases/index.asciidoc | 4 + docs/management/cases/manage-cases.asciidoc | 71 ++++++++++++++++++ docs/management/cases/setup-cases.asciidoc | 28 +++++++ docs/user/management.asciidoc | 5 ++ 7 files changed, 128 insertions(+) create mode 100644 docs/management/cases/cases.asciidoc create mode 100644 docs/management/cases/images/cases-visualization.png create mode 100644 docs/management/cases/images/cases.png create mode 100644 docs/management/cases/index.asciidoc create mode 100644 docs/management/cases/manage-cases.asciidoc create mode 100644 docs/management/cases/setup-cases.asciidoc diff --git a/docs/management/cases/cases.asciidoc b/docs/management/cases/cases.asciidoc new file mode 100644 index 0000000000000..ec61e6342f1e6 --- /dev/null +++ b/docs/management/cases/cases.asciidoc @@ -0,0 +1,20 @@ +[[cases]] +== Cases + +preview::[] + +Cases are used to open and track issues directly in {kib}. All cases list +the original reporter and all the users who contribute to a case (_participants_). +You can also send cases to third party systems by configuring external connectors. + +[role="screenshot"] +image::images/cases.png[Cases page] + +NOTE: If you create cases in the {observability} or {security-app}, they are not +visible in *{stack-manage-app}*. Likewise, the cases you create in +*{stack-manage-app}* are not visible in the {observability} or {security-app}. +You also cannot attach alerts from the {observability} or {security-app} to +cases in *{stack-manage-app}*. + +* <> +* <> \ No newline at end of file diff --git a/docs/management/cases/images/cases-visualization.png b/docs/management/cases/images/cases-visualization.png new file mode 100644 index 0000000000000000000000000000000000000000..77f249f26d091277d793565d42133e75e7c94df6 GIT binary patch literal 159476 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

, From 9f880f207f31ced758f86dc92b15f773db4b7a2f Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 9 Mar 2022 13:53:41 +0300 Subject: [PATCH 102/140] [Lens] Switch to unified metric renderer (#126019) * Switch to unified metric renderer * Fix tests * Fix snapshots * Remove unused translation * Fix tests * Fix font size mapping Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/metric_value.tsx | 2 + .../metric_vis_renderer.tsx | 1 + .../plugins/lens/common/expressions/index.ts | 1 - .../common/expressions/metric_chart/index.ts | 9 - .../expressions/metric_chart/metric_chart.ts | 113 ---- .../common/expressions/metric_chart/types.ts | 33 -- x-pack/plugins/lens/common/types.ts | 17 +- .../embeddable/embeddable_component.tsx | 3 +- x-pack/plugins/lens/public/expressions.ts | 2 - x-pack/plugins/lens/public/index.ts | 1 - .../metric_visualization/auto_scale.test.tsx | 67 --- .../metric_visualization/auto_scale.tsx | 134 ----- .../dimension_editor.test.tsx | 2 +- .../metric_visualization/dimension_editor.tsx | 3 +- .../metric_visualization/expression.scss | 87 --- .../metric_visualization/expression.test.tsx | 552 ------------------ .../metric_visualization/expression.tsx | 172 ------ .../lens/public/metric_visualization/index.ts | 14 +- .../metric_config_panel/align_options.tsx | 2 +- .../appearance_options_popover.tsx | 2 +- .../metric_config_panel/index.tsx | 2 +- .../metric_config_panel/size_options.tsx | 2 +- .../text_formatting_options.tsx | 2 +- .../title_position_option.tsx | 2 +- .../metric_suggestions.ts | 2 +- .../metric_visualization.ts | 1 - .../visualization.test.ts | 99 +++- .../metric_visualization/visualization.tsx | 110 +++- .../lens/server/expressions/expressions.ts | 2 - .../translations/translations/ja-JP.json | 6 - .../translations/translations/zh-CN.json | 6 - .../functional/apps/lens/add_to_dashboard.ts | 10 +- x-pack/test/functional/apps/lens/gauge.ts | 10 +- x-pack/test/functional/apps/lens/metrics.ts | 6 +- .../test/functional/page_objects/lens_page.ts | 12 +- 35 files changed, 228 insertions(+), 1261 deletions(-) delete mode 100644 x-pack/plugins/lens/common/expressions/metric_chart/index.ts delete mode 100644 x-pack/plugins/lens/common/expressions/metric_chart/metric_chart.ts delete mode 100644 x-pack/plugins/lens/common/expressions/metric_chart/types.ts delete mode 100644 x-pack/plugins/lens/public/metric_visualization/auto_scale.test.tsx delete mode 100644 x-pack/plugins/lens/public/metric_visualization/auto_scale.tsx delete mode 100644 x-pack/plugins/lens/public/metric_visualization/expression.scss delete mode 100644 x-pack/plugins/lens/public/metric_visualization/expression.test.tsx delete mode 100644 x-pack/plugins/lens/public/metric_visualization/expression.tsx diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx index 35c754d8b0b24..095dec8d37aab 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_value.tsx @@ -43,6 +43,7 @@ export const MetricVisValue = ({ style={autoScale && colorFullBackground ? {} : { backgroundColor: metric.bgColor }} >
{labelConfig.show && (
, - MetricRender -> = { - name: 'lens_metric_chart', - type: 'render', - help: 'A metric chart', - args: { - title: { - types: ['string'], - help: i18n.translate('xpack.lens.metric.title.help', { - defaultMessage: 'The visualization title.', - }), - }, - size: { - types: ['string'], - help: i18n.translate('xpack.lens.metric.size.help', { - defaultMessage: 'The visualization text size.', - }), - default: 'xl', - }, - titlePosition: { - types: ['string'], - help: i18n.translate('xpack.lens.metric.titlePosition.help', { - defaultMessage: 'The visualization title position.', - }), - default: 'bottom', - }, - textAlign: { - types: ['string'], - help: i18n.translate('xpack.lens.metric.textAlignPosition.help', { - defaultMessage: 'The visualization text alignment position.', - }), - default: 'center', - }, - description: { - types: ['string'], - help: '', - }, - metricTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.metric.metricTitle.help', { - defaultMessage: 'The title of the metric shown.', - }), - }, - accessor: { - types: ['string'], - help: i18n.translate('xpack.lens.metric.accessor.help', { - defaultMessage: 'The column whose value is being displayed', - }), - }, - mode: { - types: ['string'], - options: ['reduced', 'full'], - default: 'full', - help: i18n.translate('xpack.lens.metric.mode.help', { - defaultMessage: - 'The display mode of the chart - reduced will only show the metric itself without min size', - }), - }, - colorMode: { - types: ['string'], - default: `"${ColorMode.None}"`, - options: [ColorMode.None, ColorMode.Labels, ColorMode.Background], - help: i18n.translate('xpack.lens.metric.colorMode.help', { - defaultMessage: 'Which part of metric to color', - }), - }, - palette: { - types: ['palette'], - help: i18n.translate('xpack.lens.metric.palette.help', { - defaultMessage: 'Provides colors for the values', - }), - }, - }, - inputTypes: ['lens_multitable'], - fn(data, args) { - return { - type: 'render', - as: 'lens_metric_chart_renderer', - value: { - data, - args, - }, - } as MetricRender; - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/metric_chart/types.ts b/x-pack/plugins/lens/common/expressions/metric_chart/types.ts deleted file mode 100644 index 00f7d7454061f..0000000000000 --- a/x-pack/plugins/lens/common/expressions/metric_chart/types.ts +++ /dev/null @@ -1,33 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ColorMode, - CustomPaletteState, - PaletteOutput, -} from '../../../../../../src/plugins/charts/common'; -import { CustomPaletteParams, LayerType } from '../../types'; - -export interface MetricState { - layerId: string; - accessor?: string; - layerType: LayerType; - colorMode?: ColorMode; - palette?: PaletteOutput; - titlePosition?: 'top' | 'bottom'; - size?: string; - textAlign?: 'left' | 'right' | 'center'; -} - -export interface MetricConfig extends Omit { - title: string; - description: string; - metricTitle: string; - mode: 'reduced' | 'full'; - colorMode: ColorMode; - palette: PaletteOutput; -} diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index 8c333fd0daa6a..ae34331fdfb67 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -13,8 +13,11 @@ import type { SerializedFieldFormat, } from '../../../../src/plugins/field_formats/common'; import type { Datatable } from '../../../../src/plugins/expressions/common'; -import type { PaletteContinuity } from '../../../../src/plugins/charts/common'; -import type { PaletteOutput } from '../../../../src/plugins/charts/common'; +import type { + PaletteContinuity, + PaletteOutput, + ColorMode, +} from '../../../../src/plugins/charts/common'; import { CategoryDisplay, layerTypes, @@ -121,3 +124,13 @@ export interface PieVisualizationState { layers: PieLayerState[]; palette?: PaletteOutput; } +export interface MetricState { + layerId: string; + accessor?: string; + layerType: LayerType; + colorMode?: ColorMode; + palette?: PaletteOutput; + titlePosition?: 'top' | 'bottom'; + size?: string; + textAlign?: 'left' | 'right' | 'center'; +} diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index f44aef76ab83d..802e1f2b38213 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -24,8 +24,7 @@ import type { LensByReferenceInput, LensByValueInput } from './embeddable'; import type { Document } from '../persistence'; import type { IndexPatternPersistedState } from '../indexpattern_datasource/types'; import type { XYState } from '../xy_visualization/types'; -import type { MetricState } from '../../common/expressions'; -import type { PieVisualizationState } from '../../common'; +import type { PieVisualizationState, MetricState } from '../../common'; import type { DatatableVisualizationState } from '../datatable_visualization/visualization'; import type { HeatmapVisualizationState } from '../heatmap_visualization/types'; import type { GaugeVisualizationState } from '../visualizations/gauge/constants'; diff --git a/x-pack/plugins/lens/public/expressions.ts b/x-pack/plugins/lens/public/expressions.ts index 87445ff6d6882..833fcc15762af 100644 --- a/x-pack/plugins/lens/public/expressions.ts +++ b/x-pack/plugins/lens/public/expressions.ts @@ -30,7 +30,6 @@ import { renameColumns } from '../common/expressions/rename_columns/rename_colum import { formatColumn } from '../common/expressions/format_column'; import { counterRate } from '../common/expressions/counter_rate'; import { getTimeScale } from '../common/expressions/time_scale/time_scale'; -import { metricChart } from '../common/expressions/metric_chart/metric_chart'; import { lensMultitable } from '../common/expressions'; export const setupExpressions = ( @@ -44,7 +43,6 @@ export const setupExpressions = ( xyChart, mergeTables, counterRate, - metricChart, yAxisConfig, dataLayerConfig, referenceLineLayerConfig, diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index f6ccb071075ac..e9a458f6e3a24 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -14,7 +14,6 @@ export type { export type { XYState } from './xy_visualization/types'; export type { DataType, OperationMetadata, Visualization } from './types'; export type { - MetricState, AxesSettingsConfig, XYLayerConfig, LegendConfig, diff --git a/x-pack/plugins/lens/public/metric_visualization/auto_scale.test.tsx b/x-pack/plugins/lens/public/metric_visualization/auto_scale.test.tsx deleted file mode 100644 index b7584ffa9eb7e..0000000000000 --- a/x-pack/plugins/lens/public/metric_visualization/auto_scale.test.tsx +++ /dev/null @@ -1,67 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { computeScale, AutoScale } from './auto_scale'; -import { render } from 'enzyme'; - -const mockElement = (clientWidth = 100, clientHeight = 200) => ({ - clientHeight, - clientWidth, -}); - -describe('AutoScale', () => { - describe('computeScale', () => { - it('is 1 if any element is null', () => { - expect(computeScale(null, null)).toBe(1); - expect(computeScale(mockElement(), null)).toBe(1); - expect(computeScale(null, mockElement())).toBe(1); - }); - - it('is never over 1', () => { - expect(computeScale(mockElement(2000, 2000), mockElement(1000, 1000))).toBe(1); - }); - - it('is never under 0.3 in default case', () => { - expect(computeScale(mockElement(2000, 1000), mockElement(1000, 10000))).toBe(0.3); - }); - - it('is never under specified min scale if specified', () => { - expect(computeScale(mockElement(2000, 1000), mockElement(1000, 10000), 0.1)).toBe(0.1); - }); - - it('is the lesser of the x or y scale', () => { - expect(computeScale(mockElement(2000, 2000), mockElement(3000, 5000))).toBe(0.4); - expect(computeScale(mockElement(2000, 3000), mockElement(4000, 3200))).toBe(0.5); - }); - }); - - describe('AutoScale', () => { - it('renders', () => { - expect( - render( - -

Hoi!

-
- ) - ).toMatchInlineSnapshot(` -
-
-

- Hoi! -

-
-
- `); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/metric_visualization/auto_scale.tsx b/x-pack/plugins/lens/public/metric_visualization/auto_scale.tsx deleted file mode 100644 index 7e47405f9258a..0000000000000 --- a/x-pack/plugins/lens/public/metric_visualization/auto_scale.tsx +++ /dev/null @@ -1,134 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { throttle } from 'lodash'; -import classNames from 'classnames'; -import { EuiResizeObserver } from '@elastic/eui'; -import { MetricState } from '../../common/expressions'; - -interface Props extends React.HTMLAttributes { - children: React.ReactNode | React.ReactNode[]; - minScale?: number; - size?: MetricState['size']; - titlePosition?: MetricState['titlePosition']; - textAlign?: MetricState['textAlign']; -} - -interface State { - scale: number; -} - -export class AutoScale extends React.Component { - private child: Element | null = null; - private parent: Element | null = null; - private scale: () => void; - - constructor(props: Props) { - super(props); - - this.scale = throttle(() => { - const scale = computeScale(this.parent, this.child, this.props.minScale); - - // Prevent an infinite render loop - if (this.state.scale !== scale) { - this.setState({ scale }); - } - }); - - // An initial scale of 0 means we always redraw - // at least once, which is sub-optimal, but it - // prevents an annoying flicker. - this.state = { scale: 0 }; - } - - setParent = (el: Element | null) => { - if (el && this.parent !== el) { - this.parent = el; - setTimeout(() => this.scale()); - } - }; - - setChild = (el: Element | null) => { - if (el && this.child !== el) { - this.child = el; - setTimeout(() => this.scale()); - } - }; - - render() { - const { children, minScale, size, textAlign, titlePosition, ...rest } = this.props; - const { scale } = this.state; - const style = this.props.style || {}; - - return ( - - {(resizeRef) => ( -
{ - this.setParent(el); - resizeRef(el); - }} - style={{ - ...style, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - maxWidth: '100%', - maxHeight: '100%', - overflow: 'hidden', - lineHeight: 1.5, - }} - > -
- {children} -
-
- )} -
- ); - } -} - -interface ClientDimensionable { - clientWidth: number; - clientHeight: number; -} - -const MAX_SCALE = 1; -const MIN_SCALE = 0.3; - -/** - * computeScale computes the ratio by which the child needs to shrink in order - * to fit into the parent. This function is only exported for testing purposes. - */ -export function computeScale( - parent: ClientDimensionable | null, - child: ClientDimensionable | null, - minScale: number = MIN_SCALE -) { - if (!parent || !child) { - return 1; - } - - const scaleX = parent.clientWidth / child.clientWidth; - const scaleY = parent.clientHeight / child.clientHeight; - - return Math.max(Math.min(MAX_SCALE, Math.min(scaleX, scaleY)), minScale); -} diff --git a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.test.tsx b/x-pack/plugins/lens/public/metric_visualization/dimension_editor.test.tsx index b296313086d7f..478c0fff40208 100644 --- a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.test.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/dimension_editor.test.tsx @@ -16,7 +16,7 @@ import { ColorMode, PaletteOutput, PaletteRegistry } from 'src/plugins/charts/pu import { act } from 'react-dom/test-utils'; import { CustomizablePalette, PalettePanelContainer } from '../shared_components'; import { CustomPaletteParams, layerTypes } from '../../common'; -import { MetricState } from '../../common/expressions'; +import type { MetricState } from '../../common/types'; // mocking random id generator function jest.mock('@elastic/eui', () => { diff --git a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.tsx b/x-pack/plugins/lens/public/metric_visualization/dimension_editor.tsx index 77c6e909bc671..83faac5cc8026 100644 --- a/x-pack/plugins/lens/public/metric_visualization/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/dimension_editor.tsx @@ -17,7 +17,8 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback, useState } from 'react'; import { ColorMode } from '../../../../../src/plugins/charts/common'; import type { PaletteRegistry } from '../../../../../src/plugins/charts/public'; -import { isNumericFieldForDatatable, MetricState } from '../../common/expressions'; +import type { MetricState } from '../../common/types'; +import { isNumericFieldForDatatable } from '../../common/expressions'; import { applyPaletteParams, CustomizablePalette, diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.scss b/x-pack/plugins/lens/public/metric_visualization/expression.scss deleted file mode 100644 index fdd22690207fa..0000000000000 --- a/x-pack/plugins/lens/public/metric_visualization/expression.scss +++ /dev/null @@ -1,87 +0,0 @@ -.lnsMetricExpression__container { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - text-align: center; - - .lnsMetricExpression__value { - font-size: $euiFontSizeXXL * 2; - font-weight: $euiFontWeightSemiBold; - border-radius: $euiBorderRadius; - } - - .lnsMetricExpression__title { - font-size: $euiFontSizeXXL; - color: $euiTextColor; - &.reverseOrder { - order: 1; - } - } - - .lnsMetricExpression__containerScale { - display: flex; - align-items: center; - flex-direction: column; - &.alignLeft { - align-items: start; - } - &.alignRight { - align-items: end; - } - &.alignCenter { - align-items: center; - } - &.titleSizeXS { - .lnsMetricExpression__title { - font-size: $euiFontSizeXS; - } - .lnsMetricExpression__value { - font-size: $euiFontSizeXS * 2; - } - } - &.titleSizeS { - .lnsMetricExpression__title { - font-size: $euiFontSizeS; - } - .lnsMetricExpression__value { - font-size: $euiFontSizeM * 2.25; - } - } - &.titleSizeM { - .lnsMetricExpression__title { - font-size: $euiFontSizeM; - } - .lnsMetricExpression__value { - font-size: $euiFontSizeL * 2; - } - } - &.titleSizeL { - .lnsMetricExpression__title { - font-size: $euiFontSizeL; - } - .lnsMetricExpression__value { - font-size: $euiFontSizeXL * 2; - } - } - &.titleSizeXL { - .lnsMetricExpression__title { - font-size: $euiFontSizeXL; - } - .lnsMetricExpression__value { - font-size: $euiFontSizeXXL * 2; - } - } - - &.titleSizeXXL { - .lnsMetricExpression__title { - font-size: $euiFontSizeXXL; - } - .lnsMetricExpression__value { - font-size: $euiFontSizeXXL * 3; - } - } - } -} diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx deleted file mode 100644 index c37e0c6c660c9..0000000000000 --- a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx +++ /dev/null @@ -1,552 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { MetricChart } from './expression'; -import { MetricConfig, metricChart } from '../../common/expressions'; -import React from 'react'; -import { shallow, mount } from 'enzyme'; -import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; -import type { IFieldFormat } from '../../../../../src/plugins/field_formats/common'; -import { layerTypes } from '../../common'; -import type { LensMultiTable } from '../../common'; -import { IUiSettingsClient } from 'kibana/public'; -import { ColorMode } from 'src/plugins/charts/common'; - -function sampleArgs() { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - l1: { - type: 'datatable', - columns: [ - // Simulating a calculated column like a formula - { id: 'a', name: 'a', meta: { type: 'string', params: { id: 'string' } } }, - { id: 'b', name: 'b', meta: { type: 'string' } }, - { - id: 'c', - name: 'c', - meta: { type: 'number', params: { id: 'percent', params: { format: '0.000%' } } }, - }, - ], - rows: [{ a: 'last', b: 'last', c: 3 }], - }, - }, - }; - - const args: MetricConfig = { - accessor: 'c', - layerId: 'l1', - layerType: layerTypes.DATA, - title: 'My fanci metric chart', - description: 'Fancy chart description', - metricTitle: 'My fanci metric chart', - mode: 'full', - colorMode: ColorMode.None, - palette: { type: 'palette', name: 'status' }, - }; - - const noAttributesArgs: MetricConfig = { - accessor: 'c', - layerId: 'l1', - layerType: layerTypes.DATA, - title: '', - description: '', - metricTitle: 'My fanci metric chart', - mode: 'full', - colorMode: ColorMode.None, - palette: { type: 'palette', name: 'status' }, - }; - - return { data, args, noAttributesArgs }; -} - -describe('metric_expression', () => { - describe('metricChart', () => { - test('it renders with the specified data and args', () => { - const { data, args } = sampleArgs(); - const result = metricChart.fn(data, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'render', - as: 'lens_metric_chart_renderer', - value: { data, args }, - }); - }); - }); - - describe('MetricChart component', () => { - test('it renders all attributes when passed (title, description, metricTitle, value)', () => { - const { data, args } = sampleArgs(); - - expect( - shallow( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ) - ).toMatchInlineSnapshot(` - - -
- My fanci metric chart -
-
- 3 -
-
-
- `); - }); - - test('it renders strings', () => { - const { data, args } = sampleArgs(); - args.accessor = 'a'; - - expect( - shallow( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ) - ).toMatchInlineSnapshot(` - - -
- My fanci metric chart -
-
- last -
-
-
- `); - }); - - test('it renders only chart content when title and description are empty strings', () => { - const { data, noAttributesArgs } = sampleArgs(); - - expect( - shallow( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ) - ).toMatchInlineSnapshot(` - - -
- My fanci metric chart -
-
- 3 -
-
-
- `); - }); - - test('it does not render metricTitle in reduced mode', () => { - const { data, noAttributesArgs } = sampleArgs(); - - expect( - shallow( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ) - ).toMatchInlineSnapshot(` - - -
- 3 -
-
-
- `); - }); - - test('it renders an EmptyPlaceholder when no tables is passed as data', () => { - const { data, noAttributesArgs } = sampleArgs(); - - expect( - shallow( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ) - ).toMatchInlineSnapshot(` - - - - `); - }); - - test('it renders an EmptyPlaceholder when null value is passed as data', () => { - const { data, noAttributesArgs } = sampleArgs(); - - data.tables.l1.rows[0].c = null; - - expect( - shallow( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ) - ).toMatchInlineSnapshot(` - - - - `); - }); - - test('it renders 0 value', () => { - const { data, noAttributesArgs } = sampleArgs(); - - data.tables.l1.rows[0].c = 0; - - expect( - shallow( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ) - ).toMatchInlineSnapshot(` - - -
- My fanci metric chart -
-
- 0 -
-
-
- `); - }); - - test('it finds the right column to format', () => { - const { data, args } = sampleArgs(); - const factory = jest.fn(() => ({ convert: (x) => x } as IFieldFormat)); - - shallow( - - ); - expect(factory).toHaveBeenCalledWith({ id: 'percent', params: { format: '0.000%' } }); - }); - - test('it renders the correct color styling for numeric value if coloring config is passed', () => { - const { data, args } = sampleArgs(); - - args.colorMode = ColorMode.Labels; - args.palette.params = { - rangeMin: 0, - rangeMax: 400, - stops: [100, 200, 400], - gradient: false, - range: 'number', - colors: ['red', 'yellow', 'green'], - }; - - const instance = mount( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ); - - expect( - instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style') - ).toEqual( - expect.objectContaining({ - color: 'red', - }) - ); - }); - - test('it renders no color styling for numeric value if value is lower then rangeMin and continuity is "above"', () => { - const { data, args } = sampleArgs(); - - data.tables.l1.rows[0].c = -1; - args.colorMode = ColorMode.Labels; - args.palette.params = { - rangeMin: 0, - rangeMax: 400, - stops: [100, 200, 400], - gradient: false, - range: 'number', - colors: ['red', 'yellow', 'green'], - continuity: 'above', - }; - - const instance = mount( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ); - - expect( - instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style') - ).not.toEqual( - expect.objectContaining({ - color: expect.any(String), - }) - ); - }); - test('it renders no color styling for numeric value if value is higher than rangeMax and continuity is "below"', () => { - const { data, args } = sampleArgs(); - - data.tables.l1.rows[0].c = 500; - args.colorMode = ColorMode.Labels; - args.palette.params = { - rangeMin: 0, - rangeMax: 400, - stops: [100, 200, 400], - gradient: false, - range: 'number', - colors: ['red', 'yellow', 'green'], - continuity: 'below', - }; - - const instance = mount( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ); - - expect( - instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style') - ).not.toEqual( - expect.objectContaining({ - color: expect.any(String), - }) - ); - }); - - test('it renders no color styling for numeric value if value is higher than rangeMax', () => { - const { data, args } = sampleArgs(); - - data.tables.l1.rows[0].c = 500; - args.colorMode = ColorMode.Labels; - args.palette.params = { - rangeMin: 0, - rangeMax: 400, - stops: [100, 200, 400], - gradient: false, - range: 'number', - colors: ['red', 'yellow', 'green'], - }; - - const instance = mount( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ); - - expect( - instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style') - ).not.toEqual( - expect.objectContaining({ - color: expect.any(String), - }) - ); - }); - - test('it renders no color styling for numeric value if value is lower than rangeMin', () => { - const { data, args } = sampleArgs(); - - data.tables.l1.rows[0].c = -1; - args.colorMode = ColorMode.Labels; - args.palette.params = { - rangeMin: 0, - rangeMax: 400, - stops: [100, 200, 400], - gradient: false, - range: 'number', - colors: ['red', 'yellow', 'green'], - }; - - const instance = mount( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ); - - expect( - instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style') - ).not.toEqual( - expect.objectContaining({ - color: expect.any(String), - }) - ); - }); - - test('it renders the correct color styling for numeric value if user select auto detect max value', () => { - const { data, args } = sampleArgs(); - - data.tables.l1.rows[0].c = 500; - args.colorMode = ColorMode.Labels; - args.palette.params = { - rangeMin: 20, - rangeMax: Infinity, - stops: [100, 200, 400], - gradient: false, - range: 'number', - colors: ['red', 'yellow', 'green'], - }; - - const instance = mount( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ); - - expect( - instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style') - ).toEqual( - expect.objectContaining({ - color: 'green', - }) - ); - }); - - test('it renders the correct color styling for numeric value if user select auto detect min value', () => { - const { data, args } = sampleArgs(); - - data.tables.l1.rows[0].c = -1; - args.colorMode = ColorMode.Labels; - args.palette.params = { - rangeMin: -Infinity, - rangeMax: 400, - stops: [-Infinity, 200, 400], - gradient: false, - range: 'number', - colors: ['red', 'yellow', 'green'], - }; - - const instance = mount( - ({ convert: (x) => x } as IFieldFormat)} - uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient} - /> - ); - - expect( - instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style') - ).toEqual( - expect.objectContaining({ - color: 'red', - }) - ); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.tsx deleted file mode 100644 index 8a4228f0d502d..0000000000000 --- a/x-pack/plugins/lens/public/metric_visualization/expression.tsx +++ /dev/null @@ -1,172 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import './expression.scss'; -import { I18nProvider } from '@kbn/i18n-react'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import classNames from 'classnames'; -import { IUiSettingsClient, ThemeServiceStart } from 'kibana/public'; -import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; -import type { - ExpressionRenderDefinition, - IInterpreterRenderHandlers, -} from '../../../../../src/plugins/expressions/public'; -import { - ColorMode, - CustomPaletteState, - PaletteOutput, -} from '../../../../../src/plugins/charts/public'; -import { AutoScale } from './auto_scale'; -import { VisualizationContainer } from '../visualization_container'; -import { getContrastColor } from '../shared_components'; -import { EmptyPlaceholder } from '../../../../../src/plugins/charts/public'; -import { LensIconChartMetric } from '../assets/chart_metric'; -import type { FormatFactory } from '../../common'; -import type { MetricChartProps } from '../../common/expressions'; -export type { MetricChartProps, MetricState, MetricConfig } from '../../common/expressions'; - -export const getMetricChartRenderer = ( - formatFactory: FormatFactory, - uiSettings: IUiSettingsClient, - theme: ThemeServiceStart -): ExpressionRenderDefinition => ({ - name: 'lens_metric_chart_renderer', - displayName: 'Metric chart', - help: 'Metric chart renderer', - validate: () => undefined, - reuseDomNode: true, - render: (domNode: Element, config: MetricChartProps, handlers: IInterpreterRenderHandlers) => { - ReactDOM.render( - - - - - , - domNode, - () => { - handlers.done(); - } - ); - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - }, -}); - -function getColorStyling( - value: number, - colorMode: ColorMode, - palette: PaletteOutput | undefined, - isDarkTheme: boolean -) { - if ( - colorMode === ColorMode.None || - !palette?.params || - !palette?.params.colors?.length || - isNaN(value) - ) { - return {}; - } - - const { rangeMin, rangeMax, stops, colors } = palette.params; - - if (value > rangeMax) { - return {}; - } - if (value < rangeMin) { - return {}; - } - const cssProp = colorMode === ColorMode.Background ? 'backgroundColor' : 'color'; - let rawIndex = stops.findIndex((v) => v > value); - - if (!isFinite(rangeMax) && value > stops[stops.length - 1]) { - rawIndex = stops.length - 1; - } - - // in this case first stop is -Infinity - if (!isFinite(rangeMin) && value < (isFinite(stops[0]) ? stops[0] : stops[1])) { - rawIndex = 0; - } - - const colorIndex = rawIndex; - - const color = colors[colorIndex]; - const styling = { - [cssProp]: color, - }; - if (colorMode === ColorMode.Background && color) { - // set to "euiTextColor" for both light and dark color, depending on the theme - styling.color = getContrastColor(color, isDarkTheme, 'euiTextColor', 'euiTextColor'); - } - return styling; -} - -export function MetricChart({ - data, - args, - formatFactory, - uiSettings, -}: MetricChartProps & { formatFactory: FormatFactory; uiSettings: IUiSettingsClient }) { - const { metricTitle, accessor, mode, colorMode, palette, titlePosition, textAlign, size } = args; - const firstTable = Object.values(data.tables)[0]; - - const getEmptyState = () => ( - - - - ); - - if (!accessor || !firstTable) { - return getEmptyState(); - } - - const column = firstTable.columns.find(({ id }) => id === accessor); - const row = firstTable.rows[0]; - if (!column || !row) { - return getEmptyState(); - } - const rawValue = row[accessor]; - - // NOTE: Cardinality and Sum never receives "null" as value, but always 0, even for empty dataset. - // Mind falsy values here as 0! - if (!['number', 'string'].includes(typeof rawValue)) { - return getEmptyState(); - } - - const value = - column && column.meta?.params - ? formatFactory(column.meta?.params).convert(rawValue) - : Number(Number(rawValue).toFixed(3)).toString(); - - const color = getColorStyling(rawValue, colorMode, palette, uiSettings.get('theme:darkMode')); - - return ( - - - {mode === 'full' && ( -
- {metricTitle} -
- )} -
- {value} -
-
-
- ); -} diff --git a/x-pack/plugins/lens/public/metric_visualization/index.ts b/x-pack/plugins/lens/public/metric_visualization/index.ts index 8740a3af5435c..c96bb59151c20 100644 --- a/x-pack/plugins/lens/public/metric_visualization/index.ts +++ b/x-pack/plugins/lens/public/metric_visualization/index.ts @@ -6,30 +6,20 @@ */ import type { CoreSetup } from 'kibana/public'; -import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import type { EditorFrameSetup } from '../types'; -import type { FormatFactory } from '../../common'; export interface MetricVisualizationPluginSetupPlugins { - expressions: ExpressionsSetup; - formatFactory: FormatFactory; editorFrame: EditorFrameSetup; charts: ChartsPluginSetup; } export class MetricVisualization { - setup( - core: CoreSetup, - { expressions, formatFactory, editorFrame, charts }: MetricVisualizationPluginSetupPlugins - ) { + setup(core: CoreSetup, { editorFrame, charts }: MetricVisualizationPluginSetupPlugins) { editorFrame.registerVisualization(async () => { - const { getMetricVisualization, getMetricChartRenderer } = await import('../async_services'); + const { getMetricVisualization } = await import('../async_services'); const palettes = await charts.palettes.getPalettes(); - expressions.registerRenderer(() => - getMetricChartRenderer(formatFactory, core.uiSettings, core.theme) - ); return getMetricVisualization({ paletteService: palettes, theme: core.theme }); }); } diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/align_options.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/align_options.tsx index d97aa08661005..f2b97454df9df 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/align_options.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/align_options.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup } from '@elastic/eui'; -import { MetricState } from '../../../common/expressions'; +import { MetricState } from '../../../common/types'; export interface TitlePositionProps { state: MetricState; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/appearance_options_popover.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/appearance_options_popover.tsx index 973c1e0eedf39..280a036ab5daf 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/appearance_options_popover.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/appearance_options_popover.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { ToolbarPopover, TooltipWrapper } from '../../shared_components'; import { TitlePositionOptions } from './title_position_option'; import { FramePublicAPI } from '../../types'; -import { MetricState } from '../../../common/expressions'; +import type { MetricState } from '../../../common/types'; import { TextFormattingOptions } from './text_formatting_options'; export interface VisualOptionsPopoverProps { diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/index.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/index.tsx index 5a6566a863a3d..947115fcee5db 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/index.tsx @@ -8,9 +8,9 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, htmlIdGenerator } from '@elastic/eui'; import type { VisualizationToolbarProps } from '../../types'; +import type { MetricState } from '../../../common/types'; import { AppearanceOptionsPopover } from './appearance_options_popover'; -import { MetricState } from '../../../common/expressions'; export const MetricToolbar = memo(function MetricToolbar( props: VisualizationToolbarProps diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/size_options.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/size_options.tsx index d33d72751a203..17142ab77ab35 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/size_options.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/size_options.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonIcon, EuiSuperSelect } from '@elastic/eui'; -import { MetricState } from '../../../common/expressions'; +import type { MetricState } from '../../../common/types'; export interface TitlePositionProps { state: MetricState; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/text_formatting_options.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/text_formatting_options.tsx index 9215d27ebb874..13be9e59ec867 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/text_formatting_options.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/text_formatting_options.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { MetricState } from '../../../common/expressions'; +import type { MetricState } from '../../../common/types'; import { SizeOptions } from './size_options'; import { AlignOptions } from './align_options'; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/title_position_option.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/title_position_option.tsx index c35567bb69537..acaa11f477226 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/title_position_option.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/metric_config_panel/title_position_option.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; -import { MetricState } from '../../../common/expressions'; +import type { MetricState } from '../../../common/types'; export interface TitlePositionProps { state: MetricState; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts b/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts index f4a97b724ae20..49346c48e9b16 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts +++ b/x-pack/plugins/lens/public/metric_visualization/metric_suggestions.ts @@ -6,7 +6,7 @@ */ import { SuggestionRequest, VisualizationSuggestion, TableSuggestion } from '../types'; -import type { MetricState } from '../../common/expressions'; +import type { MetricState } from '../../common/types'; import { layerTypes } from '../../common'; import { LensIconChartMetric } from '../assets/chart_metric'; import { supportedTypes } from './visualization'; diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.ts b/x-pack/plugins/lens/public/metric_visualization/metric_visualization.ts index 231b6bacbbe20..78f082b8c0e29 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.ts +++ b/x-pack/plugins/lens/public/metric_visualization/metric_visualization.ts @@ -5,5 +5,4 @@ * 2.0. */ -export * from './expression'; export * from './visualization'; diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts index 83a54e4f1a3cd..c7e01b0c6a137 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts @@ -6,7 +6,7 @@ */ import { getMetricVisualization } from './visualization'; -import { MetricState } from '../../common/expressions'; +import type { MetricState } from '../../common/types'; import { layerTypes } from '../../common'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { generateId } from '../id_generator'; @@ -286,36 +286,93 @@ describe('metric_visualization', () => { "chain": Array [ Object { "arguments": Object { - "accessor": Array [ - "a", + "autoScale": Array [ + true, + ], + "colorFullBackground": Array [ + true, ], "colorMode": Array [ "None", ], - "description": Array [ - "", - ], - "metricTitle": Array [ - "shazm", + "font": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "align": Array [ + "center", + ], + "lHeight": Array [ + 127.5, + ], + "size": Array [ + 85, + ], + "sizeUnit": Array [ + "px", + ], + "weight": Array [ + "600", + ], + }, + "function": "font", + "type": "function", + }, + ], + "type": "expression", + }, ], - "mode": Array [ - "full", - ], - "palette": Array [], - "size": Array [ - "xl", + "labelFont": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "align": Array [ + "center", + ], + "lHeight": Array [ + 40.5, + ], + "size": Array [ + 27, + ], + "sizeUnit": Array [ + "px", + ], + }, + "function": "font", + "type": "function", + }, + ], + "type": "expression", + }, ], - "textAlign": Array [ - "center", + "labelPosition": Array [ + "bottom", ], - "title": Array [ - "", + "metric": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "accessor": Array [ + "a", + ], + }, + "function": "visdimension", + "type": "function", + }, + ], + "type": "expression", + }, ], - "titlePosition": Array [ - "bottom", + "palette": Array [], + "showLabels": Array [ + true, ], }, - "function": "lens_metric_chart", + "function": "metricVis", "type": "function", }, ], diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx index a3933cfa3a12f..f3a5a17818410 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx @@ -8,23 +8,45 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; +import { euiThemeVars } from '@kbn/ui-theme'; import { render } from 'react-dom'; import { Ast } from '@kbn/interpreter'; import { ThemeServiceStart } from 'kibana/public'; import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; -import { ColorMode } from '../../../../../src/plugins/charts/common'; +import { + ColorMode, + CustomPaletteState, + PaletteOutput, +} from '../../../../../src/plugins/charts/common'; import { PaletteRegistry } from '../../../../../src/plugins/charts/public'; import { getSuggestions } from './metric_suggestions'; import { LensIconChartMetric } from '../assets/chart_metric'; import { Visualization, OperationMetadata, DatasourcePublicAPI } from '../types'; -import type { MetricConfig, MetricState } from '../../common/expressions'; +import type { MetricState } from '../../common/types'; import { layerTypes } from '../../common'; import { CUSTOM_PALETTE, shiftPalette } from '../shared_components'; import { MetricDimensionEditor } from './dimension_editor'; import { MetricToolbar } from './metric_config_panel'; +interface MetricConfig extends Omit { + title: string; + description: string; + metricTitle: string; + mode: 'reduced' | 'full'; + colorMode: ColorMode; + palette: PaletteOutput; +} + export const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); +const getFontSizeAndUnit = (fontSize: string) => { + const [size, sizeUnit] = fontSize.split(/(\d+)/).filter(Boolean); + return { + size: Number(size), + sizeUnit, + }; +}; + const toExpression = ( paletteService: PaletteRegistry, state: MetricState, @@ -56,22 +78,87 @@ const toExpression = ( reverse: false, }; + const fontSizes: Record = { + xs: getFontSizeAndUnit(euiThemeVars.euiFontSizeXS), + s: getFontSizeAndUnit(euiThemeVars.euiFontSizeS), + m: getFontSizeAndUnit(euiThemeVars.euiFontSizeM), + l: getFontSizeAndUnit(euiThemeVars.euiFontSizeL), + xl: getFontSizeAndUnit(euiThemeVars.euiFontSizeXL), + xxl: getFontSizeAndUnit(euiThemeVars.euiFontSizeXXL), + }; + + const labelFont = fontSizes[state?.size || 'xl']; + const labelToMetricFontSizeMap: Record = { + xs: fontSizes.xs.size * 2, + s: fontSizes.m.size * 2.5, + m: fontSizes.l.size * 2.5, + l: fontSizes.xl.size * 2.5, + xl: fontSizes.xxl.size * 2.5, + xxl: fontSizes.xxl.size * 3, + }; + const metricFontSize = labelToMetricFontSizeMap[state?.size || 'xl']; + return { type: 'expression', chain: [ { type: 'function', - function: 'lens_metric_chart', + function: 'metricVis', arguments: { - title: [attributes?.title || ''], - size: [state?.size || 'xl'], - titlePosition: [state?.titlePosition || 'bottom'], - textAlign: [state?.textAlign || 'center'], - description: [attributes?.description || ''], - metricTitle: [operation?.label || ''], - accessor: [state.accessor], - mode: [attributes?.mode || 'full'], + labelPosition: [state?.titlePosition || 'bottom'], + font: [ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'font', + arguments: { + align: [state?.textAlign || 'center'], + size: [metricFontSize], + weight: ['600'], + lHeight: [metricFontSize * 1.5], + sizeUnit: [labelFont.sizeUnit], + }, + }, + ], + }, + ], + labelFont: [ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'font', + arguments: { + align: [state?.textAlign || 'center'], + size: [labelFont.size], + lHeight: [labelFont.size * 1.5], + sizeUnit: [labelFont.sizeUnit], + }, + }, + ], + }, + ], + metric: [ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'visdimension', + arguments: { + accessor: [state.accessor], + }, + }, + ], + }, + ], + showLabels: [!attributes?.mode || attributes?.mode === 'full'], colorMode: !canColor ? [ColorMode.None] : [state?.colorMode || ColorMode.None], + autoScale: [true], + colorFullBackground: [true], palette: state?.colorMode && state?.colorMode !== ColorMode.None ? [paletteService.get(CUSTOM_PALETTE).toExpression(paletteParams)] @@ -81,6 +168,7 @@ const toExpression = ( ], }; }; + export const getMetricVisualization = ({ paletteService, theme, diff --git a/x-pack/plugins/lens/server/expressions/expressions.ts b/x-pack/plugins/lens/server/expressions/expressions.ts index 1af99ac11e5e3..84e238b3eb15e 100644 --- a/x-pack/plugins/lens/server/expressions/expressions.ts +++ b/x-pack/plugins/lens/server/expressions/expressions.ts @@ -9,7 +9,6 @@ import type { CoreSetup } from 'kibana/server'; import { xyChart, counterRate, - metricChart, yAxisConfig, dataLayerConfig, referenceLineLayerConfig, @@ -38,7 +37,6 @@ export const setupExpressions = ( [ xyChart, counterRate, - metricChart, yAxisConfig, dataLayerConfig, referenceLineLayerConfig, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 6ab97b08c93d1..b314e0c88e686 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -711,19 +711,13 @@ "xpack.lens.layerPanel.missingDataView": "データビューが見つかりません", "xpack.lens.legacyUrlConflict.objectNoun": "レンズビジュアライゼーション", "xpack.lens.lensSavedObjectLabel": "レンズビジュアライゼーション", - "xpack.lens.metric.accessor.help": "値が表示されている列", "xpack.lens.metric.addLayer": "ビジュアライゼーションレイヤーを追加", - "xpack.lens.metric.colorMode.help": "色を変更するメトリックの部分", "xpack.lens.metric.dynamicColoring.background": "塗りつぶし", "xpack.lens.metric.dynamicColoring.label": "値別の色", "xpack.lens.metric.dynamicColoring.none": "なし", "xpack.lens.metric.dynamicColoring.text": "テキスト", "xpack.lens.metric.groupLabel": "目標値と単一の値", "xpack.lens.metric.label": "メトリック", - "xpack.lens.metric.metricTitle.help": "表示されるメトリックのタイトル。", - "xpack.lens.metric.mode.help": "グラフの表示モード。減らすと、最小サイズ内でメトリックのみが表示されます", - "xpack.lens.metric.palette.help": "値の色を指定します", - "xpack.lens.metric.title.help": "グラフタイトル。", "xpack.lens.pageTitle": "レンズ", "xpack.lens.paletteHeatmapGradient.customize": "編集", "xpack.lens.paletteHeatmapGradient.customizeLong": "パレットを編集", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 93878dc3ba43b..3797f746d4103 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -717,19 +717,13 @@ "xpack.lens.layerPanel.missingDataView": "找不到数据视图", "xpack.lens.legacyUrlConflict.objectNoun": "Lens 可视化", "xpack.lens.lensSavedObjectLabel": "Lens 可视化", - "xpack.lens.metric.accessor.help": "正显示值的列", "xpack.lens.metric.addLayer": "添加可视化图层", - "xpack.lens.metric.colorMode.help": "指标的哪部分要上色", "xpack.lens.metric.dynamicColoring.background": "填充", "xpack.lens.metric.dynamicColoring.label": "按值上色", "xpack.lens.metric.dynamicColoring.none": "无", "xpack.lens.metric.dynamicColoring.text": "文本", "xpack.lens.metric.groupLabel": "目标值和单值", "xpack.lens.metric.label": "指标", - "xpack.lens.metric.metricTitle.help": "显示的指标标题。", - "xpack.lens.metric.mode.help": "图表的显示模式 - 缩减模式将仅显示指标本身,无最小大小", - "xpack.lens.metric.palette.help": "为值提供颜色", - "xpack.lens.metric.title.help": "图标标题。", "xpack.lens.pageTitle": "Lens", "xpack.lens.paletteHeatmapGradient.customize": "编辑", "xpack.lens.paletteHeatmapGradient.customizeLong": "编辑调色板", diff --git a/x-pack/test/functional/apps/lens/add_to_dashboard.ts b/x-pack/test/functional/apps/lens/add_to_dashboard.ts index f035f844f1919..90285cc2333de 100644 --- a/x-pack/test/functional/apps/lens/add_to_dashboard.ts +++ b/x-pack/test/functional/apps/lens/add_to_dashboard.ts @@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('lnsMetric'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await PageObjects.lens.assertMetric('Average of bytes', '5,727.322'); }; @@ -304,10 +304,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('lnsMetric'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await PageObjects.lens.assertMetric('Average of bytes', '5,727.322'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await testSubjects.click('lnsApp_saveButton'); const hasOptions = await testSubjects.exists('add-to-dashboard-options'); @@ -350,10 +350,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('lnsMetric'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await PageObjects.lens.assertMetric('Average of bytes', '5,727.322'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await testSubjects.click('lnsApp_saveButton'); const hasOptions = await testSubjects.exists('add-to-dashboard-options'); diff --git a/x-pack/test/functional/apps/lens/gauge.ts b/x-pack/test/functional/apps/lens/gauge.ts index c181a067d86c3..cce05d7b9baba 100644 --- a/x-pack/test/functional/apps/lens/gauge.ts +++ b/x-pack/test/functional/apps/lens/gauge.ts @@ -37,7 +37,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should switch to gauge and render a gauge with default values', async () => { await PageObjects.lens.switchToVisualization('horizontalBullet', 'gauge'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('gaugeChart'); const elementWithInfo = await find.byCssSelector('.echScreenReaderOnly'); const textContent = await elementWithInfo.getAttribute('textContent'); expect(textContent).to.contain('Average of bytes'); // it gets default title @@ -63,25 +63,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.retrySetValue('lnsToolbarGaugeLabelMinor-select', 'custom', {}); await PageObjects.lens.retrySetValue('lnsToolbarGaugeLabelMinor', 'custom subtitle'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('gaugeChart'); await PageObjects.lens.openDimensionEditor( 'lnsGauge_goalDimensionPanel > lns-empty-dimension' ); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('gaugeChart'); await PageObjects.lens.closeDimensionEditor(); await PageObjects.lens.openDimensionEditor( 'lnsGauge_minDimensionPanel > lns-empty-dimension-suggested-value' ); await PageObjects.lens.retrySetValue('lns-indexPattern-static_value-input', '1000'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('gaugeChart'); await PageObjects.lens.closeDimensionEditor(); await PageObjects.lens.openDimensionEditor( 'lnsGauge_maxDimensionPanel > lns-empty-dimension-suggested-value' ); await PageObjects.lens.retrySetValue('lns-indexPattern-static_value-input', '25000'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('gaugeChart'); await PageObjects.lens.closeDimensionEditor(); const elementWithInfo = await find.byCssSelector('.echScreenReaderOnly'); diff --git a/x-pack/test/functional/apps/lens/metrics.ts b/x-pack/test/functional/apps/lens/metrics.ts index 19f463e3569d8..62a8b69141a58 100644 --- a/x-pack/test/functional/apps/lens/metrics.ts +++ b/x-pack/test/functional/apps/lens/metrics.ts @@ -37,21 +37,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.setValue('lnsPalettePanel_dynamicColoring_range_value_1', '21000', { clearWithKeyboard: true, }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); const styleObj = await PageObjects.lens.getMetricStyle(); expect(styleObj.color).to.be('rgb(32, 146, 128)'); }); it('should change the color when reverting the palette', async () => { await testSubjects.click('lnsPalettePanel_dynamicColoring_reverseColors'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); const styleObj = await PageObjects.lens.getMetricStyle(); expect(styleObj.color).to.be('rgb(204, 86, 66)'); }); it('should reset the color stops when changing palette to a predefined one', async () => { await PageObjects.lens.changePaletteTo('temperature'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); const styleObj = await PageObjects.lens.getMetricStyle(); expect(styleObj.color).to.be('rgb(235, 239, 245)'); }); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 1898ef10345e6..ae11939558462 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -1054,8 +1054,8 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * @param count - expected count of metric */ async assertMetric(title: string, count: string) { - await this.assertExactText('[data-test-subj="lns_metric_title"]', title); - await this.assertExactText('[data-test-subj="lns_metric_value"]', count); + await this.assertExactText('[data-test-subj="metric_label"]', title); + await this.assertExactText('[data-test-subj="metric_value"]', count); }, async setMetricDynamicColoring(coloringType: 'none' | 'labels' | 'background') { @@ -1063,7 +1063,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }, async getMetricStyle() { - const el = await testSubjects.find('lnsVisualizationContainer'); + const el = await testSubjects.find('metric_value'); const styleString = await el.getAttribute('style'); return styleString.split(';').reduce>((memo, cssLine) => { const [prop, value] = cssLine.split(':'); @@ -1152,9 +1152,11 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont expect(focusedElementText).to.eql(name); }, - async waitForVisualization() { + async waitForVisualization(visDataTestSubj?: string) { async function getRenderingCount() { - const visualizationContainer = await testSubjects.find('lnsVisualizationContainer'); + const visualizationContainer = await testSubjects.find( + visDataTestSubj || 'lnsVisualizationContainer' + ); const renderingCount = await visualizationContainer.getAttribute('data-rendering-count'); return Number(renderingCount); } From 6bcce44082cad684bfcd9040c67e4020c14239b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Wed, 9 Mar 2022 12:19:49 +0100 Subject: [PATCH 103/140] [Flaky tests] Metric's server collector's integration tests (#127139) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server_collector.test.ts | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/core/server/metrics/integration_tests/server_collector.test.ts b/src/core/server/metrics/integration_tests/server_collector.test.ts index b64e7d55560d4..713d3ed1dc96e 100644 --- a/src/core/server/metrics/integration_tests/server_collector.test.ts +++ b/src/core/server/metrics/integration_tests/server_collector.test.ts @@ -17,10 +17,7 @@ import { executionContextServiceMock } from '../../execution_context/execution_c import { ServerMetricsCollector } from '../collectors/server'; import { setTimeout as setTimeoutPromise } from 'timers/promises'; -const requestWaitDelay = 25; - -// FLAKY: https://github.com/elastic/kibana/issues/59234 -describe.skip('ServerMetricsCollector', () => { +describe('ServerMetricsCollector', () => { let server: HttpService; let collector: ServerMetricsCollector; let hapiServer: HapiServer; @@ -79,30 +76,32 @@ describe.skip('ServerMetricsCollector', () => { it('collect disconnects requests infos', async () => { const never = new Promise((resolve) => undefined); - const hitSubject = new BehaviorSubject(0); + const disconnectRequested$ = new Subject(); // Controls the number of requests in the /disconnect endpoint + const disconnectAborted$ = new Subject(); // Controls the abort event in the /disconnect endpoint router.get({ path: '/', validate: false }, async (ctx, req, res) => { return res.ok({ body: '' }); }); router.get({ path: '/disconnect', validate: false }, async (ctx, req, res) => { - hitSubject.next(hitSubject.value + 1); - await never; + disconnectRequested$.next(); + req.events.aborted$.subscribe(() => { + disconnectAborted$.next(); + }); + await never; // Never resolve the request return res.ok({ body: '' }); }); await server.start(); await sendGet('/'); - // superTest.get(path).end needs to be called with a callback to actually send the request. - const discoReq1 = sendGet('/disconnect').end(() => null); - const discoReq2 = sendGet('/disconnect').end(() => null); - - await hitSubject - .pipe( - filter((count) => count >= 2), - take(1) - ) - .toPromise(); - await delay(requestWaitDelay); // wait for the requests to send + + // Subscribe to expect 2 requests to /disconnect + const waitFor2Requests = disconnectRequested$.pipe(take(2)).toPromise(); + + const discoReq1 = sendGet('/disconnect').end(); + const discoReq2 = sendGet('/disconnect').end(); + + // Wait for 2 requests to /disconnect + await waitFor2Requests; let metrics = await collector.collect(); expect(metrics.requests).toEqual( @@ -113,8 +112,13 @@ describe.skip('ServerMetricsCollector', () => { }) ); + // Subscribe to the aborted$ event + const waitFor1stAbort = disconnectAborted$.pipe(take(1)).toPromise(); + discoReq1.abort(); - await delay(requestWaitDelay); + + // Wait for the aborted$ event + await waitFor1stAbort; metrics = await collector.collect(); expect(metrics.requests).toEqual( @@ -124,8 +128,13 @@ describe.skip('ServerMetricsCollector', () => { }) ); + // Subscribe to the aborted$ event + const waitFor2ndAbort = disconnectAborted$.pipe(take(1)).toPromise(); + discoReq2.abort(); - await delay(requestWaitDelay); + + // Wait for the aborted$ event + await waitFor2ndAbort; metrics = await collector.collect(); expect(metrics.requests).toEqual( From 299b21b889756823d0058a077ab9d48435b97b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 9 Mar 2022 12:24:08 +0100 Subject: [PATCH 104/140] [Unified observability] Migrate deprecated IndexPattern types (#124962) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/filter_label.test.tsx | 14 ++-- .../components/filter_label.tsx | 10 +-- .../configurations/default_configs.ts | 8 +- .../infra_metrics/kpi_over_time_config.ts | 2 +- .../configurations/lens_attributes.test.ts | 28 +++---- .../configurations/lens_attributes.ts | 6 +- .../mobile/device_distribution_config.ts | 6 +- .../mobile/distribution_config.ts | 4 +- .../mobile/kpi_over_time_config.ts | 4 +- .../mobile/mobile_kpi_config.test.ts | 8 +- .../rum/core_web_vitals_config.test.ts | 8 +- .../rum/core_web_vitals_config.ts | 6 +- .../rum/data_distribution_config.ts | 6 +- .../rum/kpi_over_time_config.ts | 6 +- .../synthetics/data_distribution_config.ts | 7 +- .../synthetics/kpi_over_time_config.ts | 4 +- ...index_pattern.json => test_data_view.json} | 0 .../exploratory_view/configurations/utils.ts | 42 +++++------ .../contexts/exploratory_view_config.tsx | 8 +- .../embeddable/embeddable.test.tsx | 14 ++-- .../embeddable/embeddable.tsx | 4 +- .../exploratory_view/embeddable/index.tsx | 4 +- .../exploratory_view.test.tsx | 4 +- .../exploratory_view/exploratory_view.tsx | 8 +- ...ndex_pattern.tsx => use_app_data_view.tsx} | 74 +++++++++---------- .../hooks/use_discover_link.tsx | 16 ++-- .../hooks/use_lens_attributes.test.tsx | 22 +++--- .../hooks/use_lens_attributes.ts | 20 ++--- .../shared/exploratory_view/index.tsx | 6 +- .../obsv_exploratory_view.tsx | 2 +- .../shared/exploratory_view/rtl_helpers.tsx | 45 +++++------ .../breakdown/breakdowns.test.tsx | 6 +- .../breakdown/label_breakdown.tsx | 6 +- .../columns/data_type_select.test.tsx | 4 +- .../series_editor/columns/date_picker_col.tsx | 4 +- .../columns/filter_expanded.test.tsx | 4 +- .../columns/filter_value_btn.tsx | 6 +- .../columns/incomplete_badge.tsx | 4 +- .../columns/report_definition_col.test.tsx | 8 +- .../columns/report_definition_field.tsx | 12 +-- .../columns/selected_filters.test.tsx | 6 +- .../columns/selected_filters.tsx | 8 +- .../series_editor/columns/series_actions.tsx | 8 +- .../components/labels_filter.tsx | 6 +- .../expanded_series_row.test.tsx | 4 +- .../report_metric_options.test.tsx | 10 +-- .../series_editor/report_metric_options.tsx | 24 +++--- .../series_editor/series_editor.tsx | 16 ++-- .../series_editor/use_filter_values.ts | 6 +- .../shared/exploratory_view/types.ts | 4 +- .../views/add_series_button.tsx | 8 +- .../shared/field_value_suggestions/index.tsx | 4 +- .../shared/field_value_suggestions/types.ts | 2 +- .../filter_value_label/filter_value_label.tsx | 23 +++--- .../public/hooks/use_values_list.ts | 8 +- .../observability_data_views.test.ts | 6 +- .../filters_expression_select.tsx | 2 +- .../overview/filter_group/filter_group.tsx | 2 +- .../filter_group/selected_filters.tsx | 4 +- .../rum_dashboard/local_uifilters/index.tsx | 2 +- .../local_uifilters/selected_filters.tsx | 2 +- .../local_uifilters/selected_wildcards.tsx | 2 +- 62 files changed, 303 insertions(+), 304 deletions(-) rename x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/{test_index_pattern.json => test_data_view.json} (100%) rename x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/{use_app_index_pattern.tsx => use_app_data_view.tsx} (60%) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx index 097ea89826c38..dab2a82e7c7c9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx @@ -7,14 +7,14 @@ import React from 'react'; import { fireEvent, screen, waitFor } from '@testing-library/react'; -import { mockAppIndexPattern, mockIndexPattern, mockUxSeries, render } from '../rtl_helpers'; +import { mockAppDataView, mockDataView, mockUxSeries, render } from '../rtl_helpers'; import { FilterLabel } from './filter_label'; import * as useSeriesHook from '../hooks/use_series_filters'; import { buildFilterLabel } from '../../filter_value_label/filter_value_label'; // FLAKY: https://github.com/elastic/kibana/issues/115324 describe.skip('FilterLabel', function () { - mockAppIndexPattern(); + mockAppDataView(); const invertFilter = jest.fn(); jest.spyOn(useSeriesHook, 'useSeriesFilters').mockReturnValue({ @@ -30,7 +30,7 @@ describe.skip('FilterLabel', function () { negate={false} seriesId={0} removeFilter={jest.fn()} - indexPattern={mockIndexPattern} + dataView={mockDataView} series={mockUxSeries} /> ); @@ -55,7 +55,7 @@ describe.skip('FilterLabel', function () { negate={false} seriesId={0} removeFilter={removeFilter} - indexPattern={mockIndexPattern} + dataView={mockDataView} series={mockUxSeries} /> ); @@ -79,7 +79,7 @@ describe.skip('FilterLabel', function () { negate={false} seriesId={0} removeFilter={removeFilter} - indexPattern={mockIndexPattern} + dataView={mockDataView} series={mockUxSeries} /> ); @@ -106,7 +106,7 @@ describe.skip('FilterLabel', function () { negate={true} seriesId={0} removeFilter={jest.fn()} - indexPattern={mockIndexPattern} + dataView={mockDataView} series={mockUxSeries} /> ); @@ -126,7 +126,7 @@ describe.skip('FilterLabel', function () { buildFilterLabel({ field: 'user_agent.name', label: 'Browser family', - indexPattern: mockIndexPattern, + dataView: mockDataView, value: 'Firefox', negate: false, }) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx index c6254a85de9ac..077e9be4d634a 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { IndexPattern } from '../../../../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../../../../src/plugins/data_views/common'; import { useSeriesFilters } from '../hooks/use_series_filters'; import { FilterValueLabel } from '../../filter_value_label/filter_value_label'; import { SeriesUrl } from '../types'; @@ -19,7 +19,7 @@ interface Props { series: SeriesUrl; negate: boolean; definitionFilter?: boolean; - indexPattern: IndexPattern; + dataView: DataView; removeFilter: (field: string, value: string | string[], notVal: boolean) => void; } @@ -30,15 +30,15 @@ export function FilterLabel({ field, value, negate, - indexPattern, + dataView, removeFilter, definitionFilter, }: Props) { const { invertFilter } = useSeriesFilters({ seriesId, series }); - return indexPattern ? ( + return dataView ? ( { if (!definitionFilter) invertFilter(val); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts index 93b3203ad6717..3c3a634af010e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts @@ -6,12 +6,12 @@ */ import { AppDataType, ReportViewType, SeriesConfig } from '../types'; -import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; +import type { DataView } from '../../../../../../../../src/plugins/data_views/common'; import { ReportConfigMap } from '../contexts/exploratory_view_config'; interface Props { reportType: ReportViewType; - indexPattern: IndexPattern; + dataView: DataView; dataType: AppDataType; reportConfigMap: ReportConfigMap; } @@ -19,13 +19,13 @@ interface Props { export const getDefaultConfigs = ({ reportType, dataType, - indexPattern, + dataView, reportConfigMap, }: Props): SeriesConfig => { let configResult: SeriesConfig | undefined; reportConfigMap[dataType]?.some((fn) => { - const config = fn({ indexPattern }); + const config = fn({ dataView }); if (config.reportType === reportType) { configResult = config; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/infra_metrics/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/infra_metrics/kpi_over_time_config.ts index 5111be8f9e39f..56538d252fe3c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/infra_metrics/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/infra_metrics/kpi_over_time_config.ts @@ -20,7 +20,7 @@ import { SYSTEM_MEMORY_USAGE, } from '../constants/labels'; -export function getMetricsKPIConfig({ indexPattern }: ConfigProps): SeriesConfig { +export function getMetricsKPIConfig({ dataView }: ConfigProps): SeriesConfig { return { reportType: ReportTypes.KPI, defaultSeriesType: 'area', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts index 430cf84c077ca..e2b85fdccc537 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts @@ -6,7 +6,7 @@ */ import { LayerConfig, LensAttributes } from './lens_attributes'; -import { mockAppIndexPattern, mockIndexPattern } from '../rtl_helpers'; +import { mockAppDataView, mockDataView } from '../rtl_helpers'; import { getDefaultConfigs } from './default_configs'; import { sampleAttribute } from './test_data/sample_attribute'; @@ -21,16 +21,16 @@ import { RECORDS_FIELD, REPORT_METRIC_FIELD, PERCENTILE_RANKS, ReportTypes } fro import { obsvReportConfigMap } from '../obsv_exploratory_view'; describe('Lens Attribute', () => { - mockAppIndexPattern(); + mockAppDataView(); const reportViewConfig = getDefaultConfigs({ reportType: 'data-distribution', dataType: 'ux', - indexPattern: mockIndexPattern, + dataView: mockDataView, reportConfigMap: obsvReportConfigMap, }); - reportViewConfig.baseFilters?.push(...buildExistsFilter('transaction.type', mockIndexPattern)); + reportViewConfig.baseFilters?.push(...buildExistsFilter('transaction.type', mockDataView)); let lnsAttr: LensAttributes; @@ -38,7 +38,7 @@ describe('Lens Attribute', () => { seriesConfig: reportViewConfig, seriesType: 'line', operationType: 'count', - indexPattern: mockIndexPattern, + indexPattern: mockDataView, reportDefinitions: {}, time: { from: 'now-15m', to: 'now' }, color: 'green', @@ -58,7 +58,7 @@ describe('Lens Attribute', () => { const seriesConfigKpi = getDefaultConfigs({ reportType: ReportTypes.KPI, dataType: 'ux', - indexPattern: mockIndexPattern, + dataView: mockDataView, reportConfigMap: obsvReportConfigMap, }); @@ -67,7 +67,7 @@ describe('Lens Attribute', () => { seriesConfig: seriesConfigKpi, seriesType: 'line', operationType: 'count', - indexPattern: mockIndexPattern, + indexPattern: mockDataView, reportDefinitions: { 'service.name': ['elastic-co'] }, time: { from: 'now-15m', to: 'now' }, color: 'green', @@ -83,7 +83,7 @@ describe('Lens Attribute', () => { const seriesConfigKpi = getDefaultConfigs({ reportType: ReportTypes.KPI, dataType: 'ux', - indexPattern: mockIndexPattern, + dataView: mockDataView, reportConfigMap: obsvReportConfigMap, }); @@ -95,7 +95,7 @@ describe('Lens Attribute', () => { from: 'now-1h', to: 'now', }, - indexPattern: mockIndexPattern, + indexPattern: mockDataView, name: 'ux-series-1', breakdown: 'percentile', reportDefinitions: {}, @@ -200,7 +200,7 @@ describe('Lens Attribute', () => { seriesConfig: reportViewConfig, seriesType: 'line', operationType: 'count', - indexPattern: mockIndexPattern, + indexPattern: mockDataView, reportDefinitions: { 'performance.metric': [LCP_FIELD] }, time: { from: 'now-15m', to: 'now' }, color: 'green', @@ -493,7 +493,7 @@ describe('Lens Attribute', () => { seriesConfig: reportViewConfig, seriesType: 'line', operationType: 'count', - indexPattern: mockIndexPattern, + indexPattern: mockDataView, reportDefinitions: { 'performance.metric': [LCP_FIELD] }, breakdown: USER_AGENT_NAME, time: { from: 'now-15m', to: 'now' }, @@ -507,7 +507,7 @@ describe('Lens Attribute', () => { lnsAttr.getBreakdownColumn({ sourceField: USER_AGENT_NAME, layerId: 'layer0', - indexPattern: mockIndexPattern, + indexPattern: mockDataView, labels: layerConfig.seriesConfig.labels, }); @@ -676,14 +676,14 @@ describe('Lens Attribute', () => { describe('Layer Filters', function () { it('should return expected filters', function () { reportViewConfig.baseFilters?.push( - ...buildPhrasesFilter('service.name', ['elastic', 'kibana'], mockIndexPattern) + ...buildPhrasesFilter('service.name', ['elastic', 'kibana'], mockDataView) ); const layerConfig1: LayerConfig = { seriesConfig: reportViewConfig, seriesType: 'line', operationType: 'count', - indexPattern: mockIndexPattern, + indexPattern: mockDataView, reportDefinitions: { 'performance.metric': [LCP_FIELD] }, time: { from: 'now-15m', to: 'now' }, color: 'green', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index 1058973a4432d..7f4e1543ce29e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -30,7 +30,7 @@ import { CardinalityIndexPatternColumn, } from '../../../../../../lens/public'; import { urlFiltersToKueryString } from '../utils/stringify_kueries'; -import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; +import type { DataView } from '../../../../../../../../src/plugins/data_views/common'; import { FILTER_RECORDS, USE_BREAK_DOWN_COLUMN, @@ -91,7 +91,7 @@ export interface LayerConfig { operationType?: OperationType; reportDefinitions: URLReportDefinition; time: { to: string; from: string }; - indexPattern: IndexPattern; + indexPattern: DataView; // TODO: Figure out if this can be renamed or if it's a Lens requirement selectedMetricField: string; color: string; name: string; @@ -150,7 +150,7 @@ export class LensAttributes { sourceField: string; layerId: string; labels: Record; - indexPattern: IndexPattern; + indexPattern: DataView; }): TermsIndexPatternColumn { const fieldMeta = indexPattern.getFieldByName(sourceField); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts index b66709d0e2286..ae534704976e3 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts @@ -18,7 +18,7 @@ import { SERVICE_NAME } from '../constants/elasticsearch_fieldnames'; import { MOBILE_APP, NUMBER_OF_DEVICES } from '../constants/labels'; import { MobileFields } from './mobile_fields'; -export function getMobileDeviceDistributionConfig({ indexPattern }: ConfigProps): SeriesConfig { +export function getMobileDeviceDistributionConfig({ dataView }: ConfigProps): SeriesConfig { return { reportType: ReportTypes.DEVICE_DISTRIBUTION, defaultSeriesType: 'bar', @@ -36,8 +36,8 @@ export function getMobileDeviceDistributionConfig({ indexPattern }: ConfigProps) filterFields: [...Object.keys(MobileFields), LABEL_FIELDS_FILTER], breakdownFields: Object.keys(MobileFields), baseFilters: [ - ...buildPhraseFilter('agent.name', 'iOS/swift', indexPattern), - ...buildPhraseFilter('processor.event', 'transaction', indexPattern), + ...buildPhraseFilter('agent.name', 'iOS/swift', dataView), + ...buildPhraseFilter('processor.event', 'transaction', dataView), ], labels: { ...FieldLabels, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts index f2f36f7a22abc..cf3fd0f3d2aab 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts @@ -26,7 +26,7 @@ import { import { CPU_USAGE, SYSTEM_MEMORY_USAGE, MOBILE_APP, RESPONSE_LATENCY } from '../constants/labels'; import { MobileFields } from './mobile_fields'; -export function getMobileKPIDistributionConfig({ indexPattern }: ConfigProps): SeriesConfig { +export function getMobileKPIDistributionConfig({ dataView }: ConfigProps): SeriesConfig { return { reportType: ReportTypes.DISTRIBUTION, defaultSeriesType: 'bar', @@ -43,7 +43,7 @@ export function getMobileKPIDistributionConfig({ indexPattern }: ConfigProps): S filterFields: [...Object.keys(MobileFields), LABEL_FIELDS_FILTER], breakdownFields: Object.keys(MobileFields), baseFilters: [ - ...buildPhrasesFilter('agent.name', ['iOS/swift', 'open-telemetry/swift'], indexPattern), + ...buildPhrasesFilter('agent.name', ['iOS/swift', 'open-telemetry/swift'], dataView), ], labels: { ...FieldLabels, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts index 28dbe74c2b700..4d57ca45eae64 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts @@ -32,7 +32,7 @@ import { } from '../constants/labels'; import { MobileFields } from './mobile_fields'; -export function getMobileKPIConfig({ indexPattern }: ConfigProps): SeriesConfig { +export function getMobileKPIConfig({ dataView }: ConfigProps): SeriesConfig { return { reportType: ReportTypes.KPI, defaultSeriesType: 'line', @@ -50,7 +50,7 @@ export function getMobileKPIConfig({ indexPattern }: ConfigProps): SeriesConfig filterFields: [...Object.keys(MobileFields), LABEL_FIELDS_FILTER], breakdownFields: Object.keys(MobileFields), baseFilters: [ - ...buildPhrasesFilter('agent.name', ['iOS/swift', 'open-telemetry/swift'], indexPattern), + ...buildPhrasesFilter('agent.name', ['iOS/swift', 'open-telemetry/swift'], dataView), ], labels: { ...FieldLabels, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/mobile_kpi_config.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/mobile_kpi_config.test.ts index b6f9a4f311342..2a7af16c1b9ea 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/mobile_kpi_config.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/mobile_kpi_config.test.ts @@ -5,16 +5,16 @@ * 2.0. */ -import { mockAppIndexPattern, mockIndexPattern } from '../../rtl_helpers'; +import { mockAppDataView, mockDataView } from '../../rtl_helpers'; import { LensAttributes } from '../lens_attributes'; import { METRIC_SYSTEM_MEMORY_USAGE, SERVICE_NAME } from '../constants/elasticsearch_fieldnames'; import { obsvReportConfigMap } from '../../obsv_exploratory_view'; import { testMobileKPIAttr } from '../test_data/mobile_test_attribute'; import { getLayerConfigs } from '../../hooks/use_lens_attributes'; -import { IndexPatternState } from '../../hooks/use_app_index_pattern'; +import { DataViewState } from '../../hooks/use_app_data_view'; describe('Mobile kpi config test', function () { - mockAppIndexPattern(); + mockAppDataView(); let lnsAttr: LensAttributes; @@ -31,7 +31,7 @@ describe('Mobile kpi config test', function () { ], 'kpi-over-time', {} as any, - { mobile: mockIndexPattern } as IndexPatternState, + { mobile: mockDataView } as DataViewState, obsvReportConfigMap ); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts index 0602e37f61a20..a72bfeeb0d806 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { mockAppIndexPattern, mockIndexPattern } from '../../rtl_helpers'; +import { mockAppDataView, mockDataView } from '../../rtl_helpers'; import { getDefaultConfigs } from '../default_configs'; import { LayerConfig, LensAttributes } from '../lens_attributes'; import { sampleAttributeCoreWebVital } from '../test_data/sample_attribute_cwv'; @@ -13,12 +13,12 @@ import { LCP_FIELD, SERVICE_NAME, USER_AGENT_OS } from '../constants/elasticsear import { obsvReportConfigMap } from '../../obsv_exploratory_view'; describe('Core web vital config test', function () { - mockAppIndexPattern(); + mockAppDataView(); const seriesConfig = getDefaultConfigs({ reportType: 'core-web-vitals', dataType: 'ux', - indexPattern: mockIndexPattern, + dataView: mockDataView, reportConfigMap: obsvReportConfigMap, }); @@ -29,7 +29,7 @@ describe('Core web vital config test', function () { color: 'green', name: 'test-series', breakdown: USER_AGENT_OS, - indexPattern: mockIndexPattern, + indexPattern: mockDataView, time: { from: 'now-15m', to: 'now' }, reportDefinitions: { [SERVICE_NAME]: ['elastic-co'] }, selectedMetricField: LCP_FIELD, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts index e5113211e0a62..0583ab390a0ef 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts @@ -35,7 +35,7 @@ import { } from '../constants/elasticsearch_fieldnames'; import { CLS_LABEL, FID_LABEL, LCP_LABEL } from '../constants/labels'; -export function getCoreWebVitalsConfig({ indexPattern }: ConfigProps): SeriesConfig { +export function getCoreWebVitalsConfig({ dataView }: ConfigProps): SeriesConfig { const statusPallete = euiPaletteForStatus(3); return { @@ -87,8 +87,8 @@ export function getCoreWebVitalsConfig({ indexPattern }: ConfigProps): SeriesCon URL_FULL, ], baseFilters: [ - ...buildPhraseFilter(TRANSACTION_TYPE, 'page-load', indexPattern), - ...buildPhraseFilter(PROCESSOR_EVENT, 'transaction', indexPattern), + ...buildPhraseFilter(TRANSACTION_TYPE, 'page-load', dataView), + ...buildPhraseFilter(PROCESSOR_EVENT, 'transaction', dataView), ], labels: { ...FieldLabels, [SERVICE_NAME]: 'Web Application' }, definitionFields: [SERVICE_NAME, SERVICE_ENVIRONMENT], diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/data_distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/data_distribution_config.ts index 7796b381423bf..97b6d2ddf7199 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/data_distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/data_distribution_config.ts @@ -45,7 +45,7 @@ import { WEB_APPLICATION_LABEL, } from '../constants/labels'; -export function getRumDistributionConfig({ indexPattern }: ConfigProps): SeriesConfig { +export function getRumDistributionConfig({ dataView }: ConfigProps): SeriesConfig { return { reportType: ReportTypes.DISTRIBUTION, defaultSeriesType: 'line', @@ -90,8 +90,8 @@ export function getRumDistributionConfig({ indexPattern }: ConfigProps): SeriesC { label: CLS_LABEL, id: CLS_FIELD, field: CLS_FIELD }, ], baseFilters: [ - ...buildPhraseFilter(TRANSACTION_TYPE, 'page-load', indexPattern), - ...buildPhraseFilter(PROCESSOR_EVENT, 'transaction', indexPattern), + ...buildPhraseFilter(TRANSACTION_TYPE, 'page-load', dataView), + ...buildPhraseFilter(PROCESSOR_EVENT, 'transaction', dataView), ], labels: { ...FieldLabels, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/kpi_over_time_config.ts index e78a15ed66ea4..4981c5c531551 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/kpi_over_time_config.ts @@ -48,7 +48,7 @@ import { WEB_APPLICATION_LABEL, } from '../constants/labels'; -export function getKPITrendsLensConfig({ indexPattern }: ConfigProps): SeriesConfig { +export function getKPITrendsLensConfig({ dataView }: ConfigProps): SeriesConfig { return { defaultSeriesType: 'bar_stacked', seriesTypes: [], @@ -83,8 +83,8 @@ export function getKPITrendsLensConfig({ indexPattern }: ConfigProps): SeriesCon LABEL_FIELDS_BREAKDOWN, ], baseFilters: [ - ...buildPhraseFilter(TRANSACTION_TYPE, 'page-load', indexPattern), - ...buildPhraseFilter(PROCESSOR_EVENT, 'transaction', indexPattern), + ...buildPhraseFilter(TRANSACTION_TYPE, 'page-load', dataView), + ...buildPhraseFilter(PROCESSOR_EVENT, 'transaction', dataView), ], labels: { ...FieldLabels, [SERVICE_NAME]: WEB_APPLICATION_LABEL }, definitionFields: [SERVICE_NAME, SERVICE_ENVIRONMENT], diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts index fb44da8e4327f..ead75d79582cc 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts @@ -31,10 +31,7 @@ import { } from '../constants/field_names/synthetics'; import { buildExistsFilter } from '../utils'; -export function getSyntheticsDistributionConfig({ - series, - indexPattern, -}: ConfigProps): SeriesConfig { +export function getSyntheticsDistributionConfig({ series, dataView }: ConfigProps): SeriesConfig { return { reportType: ReportTypes.DISTRIBUTION, defaultSeriesType: series?.seriesType || 'line', @@ -61,7 +58,7 @@ export function getSyntheticsDistributionConfig({ baseFilters: [], definitionFields: [ { field: 'monitor.name', nested: 'synthetics.step.name.keyword', singleSelection: true }, - { field: 'url.full', filters: buildExistsFilter('summary.up', indexPattern) }, + { field: 'url.full', filters: buildExistsFilter('summary.up', dataView) }, ], metricOptions: [ { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts index 63bd7e0cf3e81..217d34facbf0f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts @@ -51,7 +51,7 @@ export const isStepLevelMetric = (metric?: string) => { SYNTHETICS_DOCUMENT_ONLOAD, ].includes(metric); }; -export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesConfig { +export function getSyntheticsKPIConfig({ dataView }: ConfigProps): SeriesConfig { return { reportType: ReportTypes.KPI, defaultSeriesType: 'bar_stacked', @@ -78,7 +78,7 @@ export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesCon palette: { type: 'palette', name: 'status' }, definitionFields: [ { field: 'monitor.name', nested: SYNTHETICS_STEP_NAME, singleSelection: true }, - { field: 'url.full', filters: buildExistsFilter('summary.up', indexPattern) }, + { field: 'url.full', filters: buildExistsFilter('summary.up', dataView) }, ], metricOptions: [ { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/test_index_pattern.json b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/test_data_view.json similarity index 100% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/test_index_pattern.json rename to x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/test_data_view.json diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts index 29f751258e02d..e2cd05087188f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts @@ -15,7 +15,7 @@ import { } from '@kbn/es-query'; import type { ReportViewType, SeriesUrl, UrlFilter } from '../types'; import type { AllSeries, AllShortSeries } from '../hooks/use_series_storage'; -import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; +import type { DataView } from '../../../../../../../../src/plugins/data_views/common'; import { URL_KEYS } from './constants/url_constants'; import { PersistableFilter } from '../../../../../../lens/common'; @@ -78,17 +78,17 @@ export function createExploratoryViewUrl( ); } -export function buildPhraseFilter(field: string, value: string, indexPattern: IndexPattern) { - const fieldMeta = indexPattern?.fields.find((fieldT) => fieldT.name === field); +export function buildPhraseFilter(field: string, value: string, dataView: DataView) { + const fieldMeta = dataView?.fields.find((fieldT) => fieldT.name === field); if (fieldMeta) { - return [esBuildPhraseFilter(fieldMeta, value, indexPattern)]; + return [esBuildPhraseFilter(fieldMeta, value, dataView)]; } return []; } -export function getQueryFilter(field: string, value: string[], indexPattern: IndexPattern) { - const fieldMeta = indexPattern?.fields.find((fieldT) => fieldT.name === field); - if (fieldMeta && indexPattern.id) { +export function getQueryFilter(field: string, value: string[], dataView: DataView) { + const fieldMeta = dataView?.fields.find((fieldT) => fieldT.name === field); + if (fieldMeta && dataView.id) { return value.map((val) => buildQueryFilter( { @@ -97,7 +97,7 @@ export function getQueryFilter(field: string, value: string[], indexPattern: Ind query: `*${val}*`, }, }, - indexPattern.id!, + dataView.id!, '' ) ); @@ -106,21 +106,21 @@ export function getQueryFilter(field: string, value: string[], indexPattern: Ind return []; } -export function buildPhrasesFilter(field: string, value: string[], indexPattern: IndexPattern) { - const fieldMeta = indexPattern?.fields.find((fieldT) => fieldT.name === field); +export function buildPhrasesFilter(field: string, value: string[], dataView: DataView) { + const fieldMeta = dataView?.fields.find((fieldT) => fieldT.name === field); if (fieldMeta) { if (value.length === 1) { - return [esBuildPhraseFilter(fieldMeta, value[0], indexPattern)]; + return [esBuildPhraseFilter(fieldMeta, value[0], dataView)]; } - return [esBuildPhrasesFilter(fieldMeta, value, indexPattern)]; + return [esBuildPhrasesFilter(fieldMeta, value, dataView)]; } return []; } -export function buildExistsFilter(field: string, indexPattern: IndexPattern) { - const fieldMeta = indexPattern?.fields.find((fieldT) => fieldT.name === field); +export function buildExistsFilter(field: string, dataView: DataView) { + const fieldMeta = dataView?.fields.find((fieldT) => fieldT.name === field); if (fieldMeta) { - return [esBuildExistsFilter(fieldMeta, indexPattern)]; + return [esBuildExistsFilter(fieldMeta, dataView)]; } return []; } @@ -130,34 +130,34 @@ type FiltersType = Array; export function urlFilterToPersistedFilter({ urlFilters, initFilters, - indexPattern, + dataView, }: { urlFilters: UrlFilter[]; initFilters?: FiltersType; - indexPattern: IndexPattern; + dataView: DataView; }) { const parsedFilters: FiltersType = initFilters ? [...initFilters] : []; urlFilters.forEach( ({ field, values = [], notValues = [], wildcards = [], notWildcards = ([] = []) }) => { if (values.length > 0) { - const filter = buildPhrasesFilter(field, values, indexPattern); + const filter = buildPhrasesFilter(field, values, dataView); parsedFilters.push(...filter); } if (notValues.length > 0) { - const filter = buildPhrasesFilter(field, notValues, indexPattern)[0]; + const filter = buildPhrasesFilter(field, notValues, dataView)[0]; filter.meta.negate = true; parsedFilters.push(filter); } if (wildcards.length > 0) { - const filter = getQueryFilter(field, wildcards, indexPattern); + const filter = getQueryFilter(field, wildcards, dataView); parsedFilters.push(...filter); } if (notWildcards.length > 0) { - const filter = getQueryFilter(field, notWildcards, indexPattern)[0]; + const filter = getQueryFilter(field, notWildcards, dataView)[0]; filter.meta.negate = true; parsedFilters.push(filter); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/contexts/exploratory_view_config.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/contexts/exploratory_view_config.tsx index b7734e675f394..c464e4a536851 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/contexts/exploratory_view_config.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/contexts/exploratory_view_config.tsx @@ -18,21 +18,21 @@ interface ExploratoryViewContextValue { reportType: ReportViewType | typeof SELECT_REPORT_TYPE; label: string; }>; - indexPatterns: Record; + dataViews: Record; reportConfigMap: ReportConfigMap; setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; theme$: AppMountParameters['theme$']; } export const ExploratoryViewContext = createContext({ - indexPatterns: {}, + dataViews: {}, } as ExploratoryViewContextValue); export function ExploratoryViewContextProvider({ children, reportTypes, dataTypes, - indexPatterns, + dataViews, reportConfigMap, setHeaderActionMenu, theme$, @@ -40,7 +40,7 @@ export function ExploratoryViewContextProvider({ const value = { reportTypes, dataTypes, - indexPatterns, + dataViews, reportConfigMap, setHeaderActionMenu, theme$, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx index a21eeca9dcb45..fbaed83c30549 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import Embeddable from './embeddable'; import { LensPublicStart } from '../../../../../../lens/public'; -import { IndexPatternState } from '../hooks/use_app_index_pattern'; +import { DataViewState } from '../hooks/use_app_data_view'; import { render } from '../rtl_helpers'; import { AddToCaseAction } from '../header/add_to_case_action'; import { ActionTypes } from './use_actions'; @@ -77,7 +77,7 @@ const mockTimeRange = { }; const mockOwner = 'securitySolution'; const mockAppId = 'securitySolutionUI'; -const mockIndexPatterns = {} as IndexPatternState; +const mockDataViews = {} as DataViewState; const mockReportType = 'kpi-over-time'; const mockTitle = 'mockTitle'; const mockLens = { @@ -110,7 +110,7 @@ describe('Embeddable', () => { caseOwner={mockOwner} customLensAttrs={mockLensAttrs} customTimeRange={mockTimeRange} - indexPatterns={mockIndexPatterns} + indexPatterns={mockDataViews} lens={mockLens} reportType={mockReportType} title={mockTitle} @@ -128,7 +128,7 @@ describe('Embeddable', () => { caseOwner={mockOwner} customLensAttrs={mockLensAttrs} customTimeRange={mockTimeRange} - indexPatterns={mockIndexPatterns} + indexPatterns={mockDataViews} lens={mockLens} reportType={mockReportType} withActions={mockActions} @@ -146,7 +146,7 @@ describe('Embeddable', () => { caseOwner={mockOwner} customLensAttrs={mockLensAttrs} customTimeRange={mockTimeRange} - indexPatterns={mockIndexPatterns} + indexPatterns={mockDataViews} lens={mockLens} reportType={mockReportType} withActions={mockActions} @@ -181,7 +181,7 @@ describe('Embeddable', () => { caseOwner={mockOwner} customLensAttrs={mockLensAttrs} customTimeRange={mockTimeRange} - indexPatterns={mockIndexPatterns} + indexPatterns={mockDataViews} isSingleMetric={true} lens={mockLens} reportType={mockReportType} @@ -213,7 +213,7 @@ describe('Embeddable', () => { caseOwner={mockOwner} customLensAttrs={mockLensAttrs} customTimeRange={mockTimeRange} - indexPatterns={mockIndexPatterns} + indexPatterns={mockDataViews} isSingleMetric={true} lens={mockLens} reportType={mockReportType} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx index 026f7ab04d68b..84ce40f0fe6c4 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -14,7 +14,7 @@ import { AppDataType, ReportViewType } from '../types'; import { getLayerConfigs } from '../hooks/use_lens_attributes'; import { LensEmbeddableInput, LensPublicStart, XYState } from '../../../../../../lens/public'; import { OperationTypeComponent } from '../series_editor/columns/operation_type_select'; -import { IndexPatternState } from '../hooks/use_app_index_pattern'; +import { DataViewState } from '../hooks/use_app_data_view'; import { ReportConfigMap } from '../contexts/exploratory_view_config'; import { obsvReportConfigMap } from '../obsv_exploratory_view'; import { ActionTypes, useActions } from './use_actions'; @@ -46,7 +46,7 @@ export interface ExploratoryEmbeddableProps { export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps { lens: LensPublicStart; - indexPatterns: IndexPatternState; + indexPatterns: DataViewState; } // eslint-disable-next-line import/no-default-export diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx index 521e7f746fdc9..6e9a2c26580de 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx @@ -11,7 +11,7 @@ import { CoreStart } from 'kibana/public'; import type { ExploratoryEmbeddableProps, ExploratoryEmbeddableComponentProps } from './embeddable'; import { ObservabilityDataViews } from '../../../../utils/observability_data_views'; import { ObservabilityPublicPluginsStart } from '../../../../plugin'; -import type { IndexPatternState } from '../hooks/use_app_index_pattern'; +import type { DataViewState } from '../hooks/use_app_data_view'; import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; import type { AppDataType } from '../types'; @@ -30,7 +30,7 @@ export function getExploratoryViewEmbeddable( plugins: ObservabilityPublicPluginsStart ) { return (props: ExploratoryEmbeddableProps) => { - const [indexPatterns, setIndexPatterns] = useState({} as IndexPatternState); + const [indexPatterns, setIndexPatterns] = useState({} as DataViewState); const [loading, setLoading] = useState(false); const series = props.attributes && props.attributes[0]; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx index b1a1b55b7ed1e..5273cc3643cba 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { screen } from '@testing-library/dom'; -import { render, mockAppIndexPattern } from './rtl_helpers'; +import { render, mockAppDataView } from './rtl_helpers'; import { ExploratoryView } from './exploratory_view'; import * as obsvDataViews from '../../../utils/observability_data_views/observability_data_views'; import * as pluginHook from '../../../hooks/use_plugin_context'; @@ -19,7 +19,7 @@ jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ }, } as any); describe('ExploratoryView', () => { - mockAppIndexPattern(); + mockAppDataView(); beforeEach(() => { const indexPattern = createStubIndexPattern({ diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx index a383bc37880ae..4c48ab292b217 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx @@ -22,7 +22,7 @@ import { ObservabilityPublicPluginsStart } from '../../../plugin'; import { useSeriesStorage } from './hooks/use_series_storage'; import { useLensAttributes } from './hooks/use_lens_attributes'; import { TypedLensByValueInput } from '../../../../../lens/public'; -import { useAppIndexPatternContext } from './hooks/use_app_index_pattern'; +import { useAppDataViewContext } from './hooks/use_app_data_view'; import { SeriesViews } from './views/series_views'; import { LensEmbeddable } from './lens_embeddable'; import { EmptyView } from './components/empty_view'; @@ -52,7 +52,7 @@ export function ExploratoryView({ null ); - const { loadIndexPattern, loading } = useAppIndexPatternContext(); + const { loadDataView, loading } = useAppDataViewContext(); const { firstSeries, allSeries, lastRefresh, reportType, setLastRefresh } = useSeriesStorage(); @@ -68,11 +68,11 @@ export function ExploratoryView({ useEffect(() => { allSeries.forEach((seriesT) => { - loadIndexPattern({ + loadDataView({ dataType: seriesT.dataType, }); }); - }, [allSeries, loadIndexPattern]); + }, [allSeries, loadDataView]); useEffect(() => { setLensAttributes(lensAttributesT); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_data_view.tsx similarity index 60% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_data_view.tsx index 7a370bc10d865..e92b0878ba3e9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_data_view.tsx @@ -7,7 +7,7 @@ import React, { createContext, useContext, Context, useState, useCallback, useMemo } from 'react'; import { HttpFetchError } from 'kibana/public'; -import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; +import type { DataView } from '../../../../../../../../src/plugins/data_views/common'; import { AppDataType } from '../types'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { ObservabilityPublicPluginsStart } from '../../../../plugin'; @@ -17,40 +17,38 @@ import { useExploratoryView } from '../contexts/exploratory_view_config'; import { DataViewInsufficientAccessError } from '../../../../../../../../src/plugins/data_views/common'; import { getApmDataViewTitle } from '../utils/utils'; -export interface IndexPatternContext { +export interface DataViewContext { loading: boolean; - indexPatterns: IndexPatternState; - indexPatternErrors: IndexPatternErrors; + dataViews: DataViewState; + dataViewErrors: DataViewErrors; hasAppData: HasAppDataState; - loadIndexPattern: (params: { dataType: AppDataType }) => void; + loadDataView: (params: { dataType: AppDataType }) => void; } -export const IndexPatternContext = createContext>({}); +export const DataViewContext = createContext>({}); interface ProviderProps { children: JSX.Element; } type HasAppDataState = Record; -export type IndexPatternState = Record; -export type IndexPatternErrors = Record; +export type DataViewState = Record; +export type DataViewErrors = Record; type LoadingState = Record; -export function IndexPatternContextProvider({ children }: ProviderProps) { +export function DataViewContextProvider({ children }: ProviderProps) { const [loading, setLoading] = useState({} as LoadingState); - const [indexPatterns, setIndexPatterns] = useState({} as IndexPatternState); - const [indexPatternErrors, setIndexPatternErrors] = useState( - {} as IndexPatternErrors - ); + const [dataViews, setDataViews] = useState({} as DataViewState); + const [dataViewErrors, setDataViewErrors] = useState({} as DataViewErrors); const [hasAppData, setHasAppData] = useState({} as HasAppDataState); const { - services: { dataViews }, + services: { dataViews: dataViewsService }, } = useKibana(); - const { indexPatterns: indexPatternsList } = useExploratoryView(); + const { dataViews: dataViewsList } = useExploratoryView(); - const loadIndexPattern: IndexPatternContext['loadIndexPattern'] = useCallback( + const loadDataView: DataViewContext['loadDataView'] = useCallback( async ({ dataType }) => { if (typeof hasAppData[dataType] === 'undefined' && !loading[dataType]) { setLoading((prevState) => ({ ...prevState, [dataType]: true })); @@ -58,8 +56,8 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { try { let hasDataT = false; let indices: string | undefined = ''; - if (indexPatternsList[dataType]) { - indices = indexPatternsList[dataType]; + if (dataViewsList[dataType]) { + indices = dataViewsList[dataType]; hasDataT = true; } switch (dataType) { @@ -84,10 +82,10 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { setHasAppData((prevState) => ({ ...prevState, [dataType]: hasDataT })); if (hasDataT && indices) { - const obsvIndexP = new ObservabilityDataViews(dataViews); - const indPattern = await obsvIndexP.getDataView(dataType, indices); + const obsvDataV = new ObservabilityDataViews(dataViewsService); + const dataV = await obsvDataV.getDataView(dataType, indices); - setIndexPatterns((prevState) => ({ ...prevState, [dataType]: indPattern })); + setDataViews((prevState) => ({ ...prevState, [dataType]: dataV })); } setLoading((prevState) => ({ ...prevState, [dataType]: false })); } catch (e) { @@ -95,48 +93,48 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { e instanceof DataViewInsufficientAccessError || (e as HttpFetchError).body === 'Forbidden' ) { - setIndexPatternErrors((prevState) => ({ ...prevState, [dataType]: e })); + setDataViewErrors((prevState) => ({ ...prevState, [dataType]: e })); } setLoading((prevState) => ({ ...prevState, [dataType]: false })); } } }, - [dataViews, hasAppData, indexPatternsList, loading] + [dataViewsService, hasAppData, dataViewsList, loading] ); return ( - loadingT), }} > {children} - + ); } -export const useAppIndexPatternContext = (dataType?: AppDataType) => { - const { loading, hasAppData, loadIndexPattern, indexPatterns, indexPatternErrors } = useContext( - IndexPatternContext as unknown as Context +export const useAppDataViewContext = (dataType?: AppDataType) => { + const { loading, hasAppData, loadDataView, dataViews, dataViewErrors } = useContext( + DataViewContext as unknown as Context ); - if (dataType && !indexPatterns?.[dataType] && !loading) { - loadIndexPattern({ dataType }); + if (dataType && !dataViews?.[dataType] && !loading) { + loadDataView({ dataType }); } return useMemo(() => { return { hasAppData, loading, - indexPatterns, - indexPatternErrors, - indexPattern: dataType ? indexPatterns?.[dataType] : undefined, + dataViews, + dataViewErrors, + dataView: dataType ? dataViews?.[dataType] : undefined, hasData: dataType ? hasAppData?.[dataType] : undefined, - loadIndexPattern, + loadDataView, }; - }, [dataType, hasAppData, indexPatternErrors, indexPatterns, loadIndexPattern, loading]); + }, [dataType, hasAppData, dataViewErrors, dataViews, loadDataView, loading]); }; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx index eac76cc238ad7..d3a9d16be8c0f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx @@ -9,7 +9,7 @@ import { useCallback, useEffect, useState } from 'react'; import { Filter } from '@kbn/es-query'; import { useKibana } from '../../../../utils/kibana_react'; import { SeriesConfig, SeriesUrl } from '../types'; -import { useAppIndexPatternContext } from './use_app_index_pattern'; +import { useAppDataViewContext } from './use_app_data_view'; import { buildExistsFilter, urlFilterToPersistedFilter } from '../configurations/utils'; import { getFiltersFromDefs } from './use_lens_attributes'; import { RECORDS_FIELD, RECORDS_PERCENTAGE_FIELD } from '../configurations/constants'; @@ -25,21 +25,21 @@ export const useDiscoverLink = ({ series, seriesConfig }: UseDiscoverLink) => { application: { navigateToUrl }, } = kServices; - const { indexPatterns } = useAppIndexPatternContext(); + const { dataViews } = useAppDataViewContext(); const locator = kServices.discover?.locator; const [discoverUrl, setDiscoverUrl] = useState(''); useEffect(() => { - const indexPattern = indexPatterns?.[series.dataType]; + const dataView = dataViews?.[series.dataType]; - if (indexPattern) { + if (dataView) { const definitions = series.reportDefinitions ?? {}; const urlFilters = (series.filters ?? []).concat(getFiltersFromDefs(definitions)); const filters = urlFilterToPersistedFilter({ - indexPattern, + dataView, urlFilters, initFilters: seriesConfig?.baseFilters, }) as Filter[]; @@ -51,7 +51,7 @@ export const useDiscoverLink = ({ series, seriesConfig }: UseDiscoverLink) => { selectedMetricField !== RECORDS_FIELD && selectedMetricField !== RECORDS_PERCENTAGE_FIELD ) { - filters.push(buildExistsFilter(selectedMetricField, indexPattern)[0]); + filters.push(buildExistsFilter(selectedMetricField, dataView)[0]); } const getDiscoverUrl = async () => { @@ -59,14 +59,14 @@ export const useDiscoverLink = ({ series, seriesConfig }: UseDiscoverLink) => { const newUrl = await locator.getUrl({ filters, - indexPatternId: indexPattern?.id, + indexPatternId: dataView?.id, }); setDiscoverUrl(newUrl); }; getDiscoverUrl(); } }, [ - indexPatterns, + dataViews, series.dataType, series.filters, series.reportDefinitions, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx index 05b86277470a9..d50e2134546c4 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.test.tsx @@ -11,11 +11,11 @@ import { allSeriesKey, reportTypeKey, UrlStorageContextProvider } from './use_se import { renderHook } from '@testing-library/react-hooks'; import { useLensAttributes } from './use_lens_attributes'; import { ReportTypes } from '../configurations/constants'; -import { mockIndexPattern } from '../rtl_helpers'; +import { mockDataView } from '../rtl_helpers'; import { createKbnUrlStateStorage } from '../../../../../../../../src/plugins/kibana_utils/public'; import { TRANSACTION_DURATION } from '../configurations/constants/elasticsearch_fieldnames'; import * as lensAttributes from '../configurations/lens_attributes'; -import * as indexPattern from './use_app_index_pattern'; +import * as useAppDataViewHook from './use_app_data_view'; import * as theme from '../../../../hooks/use_theme'; import { dataTypes, obsvReportConfigMap, reportTypesList } from '../obsv_exploratory_view'; import { ExploratoryViewContextProvider } from '../contexts/exploratory_view_config'; @@ -35,14 +35,14 @@ const mockSingleSeries = [ describe('useExpViewTimeRange', function () { const storage = createKbnUrlStateStorage({ useHash: false }); // @ts-ignore - jest.spyOn(indexPattern, 'useAppIndexPatternContext').mockReturnValue({ - indexPatterns: { - ux: mockIndexPattern, - apm: mockIndexPattern, - mobile: mockIndexPattern, - infra_logs: mockIndexPattern, - infra_metrics: mockIndexPattern, - synthetics: mockIndexPattern, + jest.spyOn(useAppDataViewHook, 'useAppDataViewContext').mockReturnValue({ + dataViews: { + ux: mockDataView, + apm: mockDataView, + mobile: mockDataView, + infra_logs: mockDataView, + infra_metrics: mockDataView, + synthetics: mockDataView, }, }); jest.spyOn(theme, 'useTheme').mockReturnValue({ @@ -58,7 +58,7 @@ describe('useExpViewTimeRange', function () { { - const indexPattern = indexPatterns?.[series?.dataType]; + const dataView = dataViews?.[series?.dataType]; if ( - indexPattern && + dataView && !isEmpty(series.reportDefinitions) && !series.hidden && series.selectedMetricField ) { const seriesConfig = getDefaultConfigs({ reportType, - indexPattern, + dataView, dataType: series.dataType, reportConfigMap, }); @@ -70,7 +70,7 @@ export function getLayerConfigs( layerConfigs.push({ filters, - indexPattern, + indexPattern: dataView, seriesConfig, time: series.time, name: series.name, @@ -90,7 +90,7 @@ export function getLayerConfigs( export const useLensAttributes = (): TypedLensByValueInput['attributes'] | null => { const { storage, allSeries, lastRefresh, reportType } = useSeriesStorage(); - const { indexPatterns } = useAppIndexPatternContext(); + const { dataViews } = useAppDataViewContext(); const { reportConfigMap } = useExploratoryView(); @@ -101,14 +101,14 @@ export const useLensAttributes = (): TypedLensByValueInput['attributes'] | null const allSeriesT: AllSeries = convertAllShortSeries(storage.get(allSeriesKey) ?? []); const reportTypeT: ReportViewType = storage.get(reportTypeKey) as ReportViewType; - if (isEmpty(indexPatterns) || isEmpty(allSeriesT) || !reportTypeT) { + if (isEmpty(dataViews) || isEmpty(allSeriesT) || !reportTypeT) { return null; } const layerConfigs = getLayerConfigs( allSeriesT, reportTypeT, theme, - indexPatterns, + dataViews, reportConfigMap ); @@ -121,5 +121,5 @@ export const useLensAttributes = (): TypedLensByValueInput['attributes'] | null return lensAttributes.getJSON(); // we also want to check the state on allSeries changes // eslint-disable-next-line react-hooks/exhaustive-deps - }, [indexPatterns, reportType, storage, theme, lastRefresh, allSeries]); + }, [dataViews, reportType, storage, theme, lastRefresh, allSeries]); }; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx index 4fc5293e03723..94954ff5522dc 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx @@ -12,7 +12,7 @@ import { ExploratoryView } from './exploratory_view'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ObservabilityPublicPluginsStart } from '../../../plugin'; import { useBreadcrumbs } from '../../../hooks/use_breadcrumbs'; -import { IndexPatternContextProvider } from './hooks/use_app_index_pattern'; +import { DataViewContextProvider } from './hooks/use_app_data_view'; import { createKbnUrlStateStorage, withNotifyOnErrors, @@ -73,11 +73,11 @@ export function ExploratoryViewPage({ return ( - + - + ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/obsv_exploratory_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/obsv_exploratory_view.tsx index 79c37d5bb0b72..a8deb76432672 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/obsv_exploratory_view.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/obsv_exploratory_view.tsx @@ -101,7 +101,7 @@ export function ObservabilityExploratoryView() { >({ core, kibanaProps, }: MockKibanaProviderProps) { - const indexPattern = mockIndexPattern; + const dataView = mockDataView; setIndexPatterns({ - ...[indexPattern], - get: async () => indexPattern, - } as unknown as IndexPatternsContract); + ...[dataView], + get: async () => dataView, + } as unknown as DataViewsContract); return ( - {children} + {children} @@ -211,7 +214,7 @@ export function render( { return { spy, onRefreshTimeRange }; }; -export const mockAppIndexPattern = (props?: Partial) => { - const loadIndexPattern = jest.fn(); - const spy = jest.spyOn(useAppIndexPatternHook, 'useAppIndexPatternContext').mockReturnValue({ - indexPattern: mockIndexPattern, +export const mockAppDataView = (props?: Partial) => { + const loadDataView = jest.fn(); + const spy = jest.spyOn(useAppDataViewHook, 'useAppDataViewContext').mockReturnValue({ + dataView: mockDataView, hasData: true, loading: false, hasAppData: { ux: true } as any, - loadIndexPattern, - indexPatterns: { ux: mockIndexPattern } as unknown as Record, - indexPatternErrors: {} as any, + loadDataView, + dataViews: { ux: mockDataView } as unknown as Record, + dataViewErrors: {} as any, ...(props || {}), }); - return { spy, loadIndexPattern }; + return { spy, loadDataView }; }; export const mockUseValuesList = (values?: ListItem[]) => { @@ -369,12 +372,12 @@ export const mockHistory = { }, }; -export const mockIndexPattern = createStubIndexPattern({ +export const mockDataView = createStubDataView({ spec: { id: 'apm-*', title: 'apm-*', timeFieldName: '@timestamp', - fields: JSON.parse(indexPatternData.attributes.fields), + fields: JSON.parse(dataViewData.attributes.fields), }, }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.test.tsx index e213a41238123..37a554ba334d1 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import { Breakdowns } from './breakdowns'; -import { mockIndexPattern, mockUxSeries, render } from '../../rtl_helpers'; +import { mockDataView, mockUxSeries, render } from '../../rtl_helpers'; import { getDefaultConfigs } from '../../configurations/default_configs'; import { RECORDS_FIELD } from '../../configurations/constants'; import { USER_AGENT_OS } from '../../configurations/constants/elasticsearch_fieldnames'; @@ -17,7 +17,7 @@ import { obsvReportConfigMap } from '../../obsv_exploratory_view'; describe('Breakdowns', function () { const dataViewSeries = getDefaultConfigs({ reportType: 'data-distribution', - indexPattern: mockIndexPattern, + dataView: mockDataView, dataType: 'ux', reportConfigMap: obsvReportConfigMap, }); @@ -62,7 +62,7 @@ describe('Breakdowns', function () { it('does not show percentile breakdown for records metrics', function () { const kpiConfig = getDefaultConfigs({ reportType: 'kpi-over-time', - indexPattern: mockIndexPattern, + dataView: mockDataView, dataType: 'ux', reportConfigMap: obsvReportConfigMap, }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/label_breakdown.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/label_breakdown.tsx index a5723ccb52648..d85c7fcaad721 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/label_breakdown.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/label_breakdown.tsx @@ -9,7 +9,7 @@ import { EuiComboBox, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { SeriesConfig, SeriesUrl } from '../../types'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../../hooks/use_app_data_view'; import { useSeriesStorage } from '../../hooks/use_series_storage'; import { LABEL_FIELDS_BREAKDOWN } from '../../configurations/constants'; @@ -19,9 +19,9 @@ interface Props { seriesConfig?: SeriesConfig; } export function LabelsBreakdown({ series, seriesId }: Props) { - const { indexPattern } = useAppIndexPatternContext(series.dataType); + const { dataView } = useAppDataViewContext(series.dataType); - const labelFields = indexPattern?.fields.filter((field) => field.name.startsWith('labels.')); + const labelFields = dataView?.fields.filter((field) => field.name.startsWith('labels.')); const { setSeries } = useSeriesStorage(); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.test.tsx index 1afe1f979b272..9856cdd527232 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; -import { mockAppIndexPattern, mockUxSeries, render } from '../../rtl_helpers'; +import { mockAppDataView, mockUxSeries, render } from '../../rtl_helpers'; import { DataTypesSelect } from './data_type_select'; import { DataTypes } from '../../configurations/constants'; import { DataTypesLabels } from '../../obsv_exploratory_view'; @@ -15,7 +15,7 @@ import { DataTypesLabels } from '../../obsv_exploratory_view'; describe('DataTypeSelect', function () { const seriesId = 0; - mockAppIndexPattern(); + mockAppDataView(); it('should render properly', function () { render(); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx index b01010e4b81f9..51728d628199f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx @@ -14,7 +14,7 @@ import { DateRangePicker } from '../../components/date_range_picker'; import { SeriesDatePicker } from '../../components/series_date_picker'; import { AppDataType, SeriesUrl } from '../../types'; import { ReportTypes } from '../../configurations/constants'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../../hooks/use_app_data_view'; import { SyntheticsAddData } from '../../../add_data_buttons/synthetics_add_data'; import { MobileAddData } from '../../../add_data_buttons/mobile_add_data'; import { UXAddData } from '../../../add_data_buttons/ux_add_data'; @@ -36,7 +36,7 @@ const AddDataComponents: Record = { export function DatePickerCol({ seriesId, series }: Props) { const { reportType } = useSeriesStorage(); - const { hasAppData } = useAppIndexPatternContext(); + const { hasAppData } = useAppDataViewContext(); if (!series.dataType) { return null; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx index 0a5ac137a7870..d821e65afae04 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { fireEvent, screen, waitFor } from '@testing-library/react'; import { FilterExpanded } from './filter_expanded'; -import { mockUxSeries, mockAppIndexPattern, mockUseValuesList, render } from '../../rtl_helpers'; +import { mockUxSeries, mockAppDataView, mockUseValuesList, render } from '../../rtl_helpers'; import { USER_AGENT_NAME } from '../../configurations/constants/elasticsearch_fieldnames'; describe('FilterExpanded', function () { @@ -18,7 +18,7 @@ describe('FilterExpanded', function () { it('render', async () => { const initSeries = { filters }; - mockAppIndexPattern(); + mockAppDataView(); render( ) : ( button diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/incomplete_badge.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/incomplete_badge.tsx index 8e64f4bcea680..83319dc48d7df 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/incomplete_badge.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/incomplete_badge.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { isEmpty } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiBadge } from '@elastic/eui'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../../hooks/use_app_data_view'; import { SeriesConfig, SeriesUrl } from '../../types'; interface Props { @@ -18,7 +18,7 @@ interface Props { } export function IncompleteBadge({ seriesConfig, series }: Props) { - const { loading } = useAppIndexPatternContext(); + const { loading } = useAppDataViewContext(); if (!seriesConfig) { return null; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx index 3ec2af4a8c9d2..980c02a0ab153 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx @@ -9,8 +9,8 @@ import { fireEvent, screen, waitFor } from '@testing-library/react'; import React from 'react'; import { getDefaultConfigs } from '../../configurations/default_configs'; import { - mockAppIndexPattern, - mockIndexPattern, + mockAppDataView, + mockDataView, mockUseValuesList, mockUxSeries, render, @@ -19,12 +19,12 @@ import { ReportDefinitionCol } from './report_definition_col'; import { obsvReportConfigMap } from '../../obsv_exploratory_view'; describe('Series Builder ReportDefinitionCol', function () { - mockAppIndexPattern(); + mockAppDataView(); const seriesId = 0; const seriesConfig = getDefaultConfigs({ reportType: 'data-distribution', - indexPattern: mockIndexPattern, + dataView: mockDataView, dataType: 'ux', reportConfigMap: obsvReportConfigMap, }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx index 2808dfae83527..b518993a51940 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx @@ -9,7 +9,7 @@ import React, { useMemo } from 'react'; import { isEmpty } from 'lodash'; import { ExistsFilter, PhraseFilter } from '@kbn/es-query'; import FieldValueSuggestions from '../../../field_value_suggestions'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../../hooks/use_app_data_view'; import { ESFilter } from '../../../../../../../../../src/core/types/elasticsearch'; import { PersistableFilter } from '../../../../../../../lens/common'; import { buildPhrasesFilter } from '../../configurations/utils'; @@ -36,7 +36,7 @@ export function ReportDefinitionField({ onChange, filters, }: Props) { - const { indexPattern } = useAppIndexPatternContext(series.dataType); + const { dataView } = useAppDataViewContext(series.dataType); const field = typeof fieldProp === 'string' ? fieldProp : fieldProp.field; @@ -62,10 +62,10 @@ export function ReportDefinitionField({ definitionFields.forEach((fieldObj) => { const fieldT = typeof fieldObj === 'string' ? fieldObj : fieldObj.field; - if (indexPattern && selectedReportDefinitions?.[fieldT] && fieldT !== field) { + if (dataView && selectedReportDefinitions?.[fieldT] && fieldT !== field) { const values = selectedReportDefinitions?.[fieldT]; if (!values.includes(ALL_VALUES_SELECTED)) { - const valueFilter = buildPhrasesFilter(fieldT, values, indexPattern)[0]; + const valueFilter = buildPhrasesFilter(fieldT, values, dataView)[0]; if (valueFilter.query) { filtersN.push(valueFilter.query); } @@ -78,7 +78,7 @@ export function ReportDefinitionField({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(selectedReportDefinitions), JSON.stringify(baseFilters)]); - if (!indexPattern) { + if (!dataView) { return null; } @@ -86,7 +86,7 @@ export function ReportDefinitionField({ onChange(field, val)} filters={queryFilters} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/selected_filters.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/selected_filters.test.tsx index 9fcf4b14353b9..ef0f7c47d3f67 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/selected_filters.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/selected_filters.test.tsx @@ -7,18 +7,18 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; -import { mockAppIndexPattern, mockIndexPattern, mockUxSeries, render } from '../../rtl_helpers'; +import { mockAppDataView, mockDataView, mockUxSeries, render } from '../../rtl_helpers'; import { SelectedFilters } from './selected_filters'; import { getDefaultConfigs } from '../../configurations/default_configs'; import { USER_AGENT_NAME } from '../../configurations/constants/elasticsearch_fieldnames'; import { obsvReportConfigMap } from '../../obsv_exploratory_view'; describe('SelectedFilters', function () { - mockAppIndexPattern(); + mockAppDataView(); const dataViewSeries = getDefaultConfigs({ reportType: 'data-distribution', - indexPattern: mockIndexPattern, + dataView: mockDataView, dataType: 'ux', reportConfigMap: obsvReportConfigMap, }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/selected_filters.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/selected_filters.tsx index 5bba0b9dfb3cd..241a9ad13f98d 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/selected_filters.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/selected_filters.tsx @@ -10,7 +10,7 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/e import { i18n } from '@kbn/i18n'; import { FilterLabel } from '../../components/filter_label'; import { SeriesConfig, SeriesUrl, UrlFilter } from '../../types'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../../hooks/use_app_data_view'; import { useSeriesFilters } from '../../hooks/use_series_filters'; import { useSeriesStorage } from '../../hooks/use_series_storage'; @@ -28,16 +28,16 @@ export function SelectedFilters({ seriesId, series, seriesConfig }: Props) { const { removeFilter, replaceFilter } = useSeriesFilters({ seriesId, series }); - const { indexPattern } = useAppIndexPatternContext(series.dataType); + const { dataView } = useAppDataViewContext(series.dataType); - if (filters.length === 0 || !indexPattern) { + if (filters.length === 0 || !dataView) { return null; } const btnProps = { seriesId, series, - indexPattern, + dataView, }; return ( diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_actions.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_actions.tsx index b3430da2d35e2..129fdf1cf25e3 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_actions.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_actions.tsx @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { useSeriesStorage } from '../../hooks/use_series_storage'; import { SeriesConfig, SeriesUrl } from '../../types'; import { useDiscoverLink } from '../../hooks/use_discover_link'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../../hooks/use_app_data_view'; interface Props { seriesId: number; @@ -34,9 +34,9 @@ export function SeriesActions({ seriesId, series, seriesConfig, onEditClick }: P const { href: discoverHref } = useDiscoverLink({ series, seriesConfig }); - const { indexPatterns } = useAppIndexPatternContext(); + const { dataViews } = useAppDataViewContext(); - const indexPattern = indexPatterns?.[series.dataType]; + const dataView = dataViews?.[series.dataType]; const deleteDisabled = seriesId === 0 && allSeries.length > 1; const copySeries = () => { @@ -109,7 +109,7 @@ export function SeriesActions({ seriesId, series, seriesConfig, onEditClick }: P icon="discoverApp" href={discoverHref} aria-label={VIEW_SAMPLE_DOCUMENTS_LABEL} - disabled={!series.dataType || !series.selectedMetricField || !indexPattern} + disabled={!series.dataType || !series.selectedMetricField || !dataView} target="_blank" > {VIEW_SAMPLE_DOCUMENTS_LABEL} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/components/labels_filter.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/components/labels_filter.tsx index 6abe2e8f2a7d9..2284b06433e6e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/components/labels_filter.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/components/labels_filter.tsx @@ -19,7 +19,7 @@ import { EuiSelectable } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FilterProps } from '../columns/filter_expanded'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../../hooks/use_app_data_view'; import { FilterValuesList } from './filter_values_list'; import { useFilterValues } from '../use_filter_values'; @@ -28,9 +28,9 @@ export function LabelsFieldFilter(props: FilterProps) { const [query, setQuery] = useState(''); - const { indexPattern } = useAppIndexPatternContext(series.dataType); + const { dataView } = useAppDataViewContext(series.dataType); - const labelFields = indexPattern?.fields.filter((field) => field.name.startsWith('labels.')); + const labelFields = dataView?.fields.filter((field) => field.name.startsWith('labels.')); const [isPopoverOpen, setPopover] = useState(false); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.test.tsx index afb1043e9caab..5d70e42808ea9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { screen } from '@testing-library/react'; import { ExpandedSeriesRow } from './expanded_series_row'; -import { mockIndexPattern, mockUxSeries, render } from '../rtl_helpers'; +import { mockDataView, mockUxSeries, render } from '../rtl_helpers'; import { getDefaultConfigs } from '../configurations/default_configs'; import { PERCENTILE } from '../configurations/constants'; import { obsvReportConfigMap } from '../obsv_exploratory_view'; @@ -17,7 +17,7 @@ describe('ExpandedSeriesRow', function () { const dataViewSeries = getDefaultConfigs({ reportConfigMap: obsvReportConfigMap, reportType: 'kpi-over-time', - indexPattern: mockIndexPattern, + dataView: mockDataView, dataType: 'ux', }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.test.tsx index 9d83113f2b524..63725346ba18b 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { mockAppIndexPattern, mockIndexPattern, mockUxSeries, render } from '../rtl_helpers'; +import { mockAppDataView, mockDataView, mockUxSeries, render } from '../rtl_helpers'; import { getDefaultConfigs } from '../configurations/default_configs'; import { PERCENTILE } from '../configurations/constants'; import { ReportMetricOptions } from './report_metric_options'; @@ -18,7 +18,7 @@ describe('ReportMetricOptions', function () { const dataViewSeries = getDefaultConfigs({ dataType: 'ux', reportType: 'kpi-over-time', - indexPattern: mockIndexPattern, + dataView: mockDataView, reportConfigMap: obsvReportConfigMap, }); @@ -31,7 +31,7 @@ describe('ReportMetricOptions', function () { }); it('should display loading if index pattern is not available and is loading', async function () { - mockAppIndexPattern({ loading: true, indexPatterns: undefined }); + mockAppDataView({ loading: true, dataViews: undefined }); const { container } = render( { setSeries(seriesId, { @@ -52,14 +52,14 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { return null; } - const indexPattern = indexPatterns?.[series.dataType]; - const indexPatternError = indexPatternErrors?.[series.dataType]; + const dataView = dataViews?.[series.dataType]; + const dataViewError = dataViewErrors?.[series.dataType]; const options = (metricOptions ?? []).map(({ label, field, id }) => { let disabled = false; if (field !== RECORDS_FIELD && field !== RECORDS_PERCENTAGE_FIELD && field) { - disabled = !Boolean(indexPattern?.getFieldByName(field)); + disabled = !Boolean(dataView?.getFieldByName(field)); } return { disabled, @@ -85,19 +85,19 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { }; }); - if (indexPatternError && !indexPattern && !loading) { + if (dataViewError && !dataView && !loading) { // TODO: Add a link to docs to explain how to add index patterns return ( - {indexPatternError.body?.error === 'Forbidden' || - indexPatternError.name === 'DataViewInsufficientAccessError' + {dataViewError.body?.error === 'Forbidden' || + dataViewError.name === 'DataViewInsufficientAccessError' ? NO_PERMISSIONS - : indexPatternError.body.message} + : dataViewError.body.message} ); } - if (!indexPattern && !loading) { + if (!dataView && !loading) { return {NO_DATA_AVAILABLE}; } @@ -111,7 +111,7 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { onClick={() => setShowOptions((prevState) => !prevState)} fill size="s" - isLoading={!indexPattern && loading} + isLoading={!dataView && loading} buttonRef={focusButton} > {SELECT_REPORT_METRIC_LABEL} @@ -133,7 +133,7 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { )} {series.selectedMetricField && - (indexPattern ? ( + (dataView ? ( ; export const getSeriesToEdit = ({ - indexPatterns, + dataViews, allSeries, reportType, reportConfigMap, }: { allSeries: SeriesContextValue['allSeries']; - indexPatterns: IndexPatternState; + dataViews: DataViewState; reportType: ReportViewType; reportConfigMap: ReportConfigMap; }): BuilderItem[] => { const getDataViewSeries = (dataType: AppDataType) => { - if (indexPatterns?.[dataType]) { + if (dataViews?.[dataType]) { return getDefaultConfigs({ dataType, reportType, reportConfigMap, - indexPattern: indexPatterns[dataType], + dataView: dataViews[dataType], }); } }; @@ -61,7 +61,7 @@ export const SeriesEditor = React.memo(function () { const { getSeries, allSeries, reportType } = useSeriesStorage(); - const { loading, indexPatterns } = useAppIndexPatternContext(); + const { loading, dataViews } = useAppDataViewContext(); const { reportConfigMap } = useExploratoryView(); @@ -88,7 +88,7 @@ export const SeriesEditor = React.memo(function () { const newEditorItems = getSeriesToEdit({ reportType, allSeries, - indexPatterns, + dataViews, reportConfigMap, }); @@ -108,7 +108,7 @@ export const SeriesEditor = React.memo(function () { setItemIdToExpandedRowMap((prevState) => { return { ...prevState, ...newExpandRows }; }); - }, [allSeries, getSeries, indexPatterns, loading, reportConfigMap, reportType]); + }, [allSeries, getSeries, dataViews, loading, reportConfigMap, reportType]); const toggleDetails = (item: BuilderItem) => { const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts index 3a273cc6adc96..848bd6b44ea16 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/use_filter_values.ts @@ -8,7 +8,7 @@ import { ExistsFilter, isExistsFilter } from '@kbn/es-query'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { useValuesList } from '../../../../hooks/use_values_list'; import { FilterProps } from './columns/filter_expanded'; -import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../hooks/use_app_data_view'; import { ESFilter } from '../../../../../../../../src/core/types/elasticsearch'; import { PersistableFilter } from '../../../../../../lens/common'; @@ -16,7 +16,7 @@ export function useFilterValues( { field, series, baseFilters, label }: FilterProps, query?: string ) { - const { indexPatterns } = useAppIndexPatternContext(series.dataType); + const { dataViews } = useAppDataViewContext(series.dataType); const queryFilters: ESFilter[] = []; @@ -36,6 +36,6 @@ export function useFilterValues( time: series.time, keepHistory: true, filters: queryFilters, - indexPatternTitle: indexPatterns[series.dataType]?.title, + dataViewTitle: dataViews[series.dataType]?.title, }); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index acd49fc25588e..9fa565e4eae34 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -17,7 +17,7 @@ import { } from '../../../../../lens/public'; import { PersistableFilter } from '../../../../../lens/common'; -import { IndexPattern } from '../../../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../../../src/plugins/data_views/common'; export const ReportViewTypes = { dist: 'data-distribution', @@ -102,7 +102,7 @@ export interface UrlFilter { } export interface ConfigProps { - indexPattern: IndexPattern; + dataView: DataView; series?: SeriesUrl; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/views/add_series_button.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/views/add_series_button.tsx index 4c1bc1d7fe3bb..7199a2c47adee 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/views/add_series_button.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/views/add_series_button.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import { SeriesUrl, BuilderItem } from '../types'; import { getSeriesToEdit } from '../series_editor/series_editor'; import { NEW_SERIES_KEY, useSeriesStorage } from '../hooks/use_series_storage'; -import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; +import { useAppDataViewContext } from '../hooks/use_app_data_view'; import { DEFAULT_TIME, ReportTypes } from '../configurations/constants'; import { useExploratoryView } from '../contexts/exploratory_view_config'; @@ -21,13 +21,13 @@ export function AddSeriesButton() { const addSeriesButtonRef = useRef(null); const { getSeries, allSeries, setSeries, reportType } = useSeriesStorage(); - const { loading, indexPatterns } = useAppIndexPatternContext(); + const { loading, dataViews } = useAppDataViewContext(); const { reportConfigMap } = useExploratoryView(); useEffect(() => { - setEditorItems(getSeriesToEdit({ allSeries, indexPatterns, reportType, reportConfigMap })); - }, [allSeries, getSeries, indexPatterns, loading, reportConfigMap, reportType]); + setEditorItems(getSeriesToEdit({ allSeries, dataViews, reportType, reportConfigMap })); + }, [allSeries, getSeries, dataViews, loading, reportConfigMap, reportType]); const addSeries = () => { const prevSeries = allSeries?.[0]; diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.tsx b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.tsx index f2a30f8bee351..2cd8487c57048 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.tsx @@ -15,7 +15,7 @@ export function FieldValueSuggestions({ fullWidth, sourceField, label, - indexPatternTitle, + dataViewTitle, selectedValue, excludedValue, filters, @@ -41,7 +41,7 @@ export function FieldValueSuggestions({ const [query, setQuery] = useState(''); const { values, loading } = useValuesList({ - indexPatternTitle, + dataViewTitle, query, sourceField, filters, diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts index 95b24aa69b1e7..1ee477e05fbc0 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts @@ -33,7 +33,7 @@ interface CommonProps { } export type FieldValueSuggestionsProps = CommonProps & { - indexPatternTitle?: string; + dataViewTitle?: string; sourceField: string; asCombobox?: boolean; onChange: (val?: string[], excludedValue?: string[]) => void; diff --git a/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx b/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx index bd3e1d6c8f50d..f7cfe7b848fc9 100644 --- a/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx +++ b/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx @@ -8,28 +8,29 @@ import React from 'react'; import { injectI18n } from '@kbn/i18n-react'; import { Filter, buildPhrasesFilter, buildPhraseFilter } from '@kbn/es-query'; -import { FilterItem, IndexPattern } from '../../../../../../../src/plugins/data/public'; +import { FilterItem } from '../../../../../../../src/plugins/data/public'; +import type { DataView } from '../../../../../../../src/plugins/data_views/common'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; export function buildFilterLabel({ field, value, label, - indexPattern, + dataView, negate, }: { label: string; value: string | string[]; negate: boolean; field: string; - indexPattern: IndexPattern; + dataView: DataView; }) { - const indexField = indexPattern.getFieldByName(field)!; + const indexField = dataView.getFieldByName(field)!; const filter = value instanceof Array && value.length > 1 - ? buildPhrasesFilter(indexField, value, indexPattern) - : buildPhraseFilter(indexField, value as string, indexPattern); + ? buildPhrasesFilter(indexField, value, dataView) + : buildPhraseFilter(indexField, value as string, dataView); filter.meta.type = value instanceof Array && value.length > 1 ? 'phrases' : 'phrase'; @@ -49,7 +50,7 @@ export interface FilterValueLabelProps { negate: boolean; removeFilter: (field: string, value: string | string[], notVal: boolean) => void; invertFilter: (val: { field: string; value: string | string[]; negate: boolean }) => void; - indexPattern: IndexPattern; + dataView: DataView; allowExclusion?: boolean; } export function FilterValueLabel({ @@ -57,22 +58,22 @@ export function FilterValueLabel({ field, value, negate, - indexPattern, + dataView, invertFilter, removeFilter, allowExclusion = true, }: FilterValueLabelProps) { const FilterItemI18n = injectI18n(FilterItem); - const filter = buildFilterLabel({ field, value, label, indexPattern, negate }); + const filter = buildFilterLabel({ field, value, label, dataView, negate }); const { services: { uiSettings }, } = useKibana(); - return indexPattern ? ( + return dataView ? ( { diff --git a/x-pack/plugins/observability/public/hooks/use_values_list.ts b/x-pack/plugins/observability/public/hooks/use_values_list.ts index e2268f7b85244..5ab9b41ca6f9a 100644 --- a/x-pack/plugins/observability/public/hooks/use_values_list.ts +++ b/x-pack/plugins/observability/public/hooks/use_values_list.ts @@ -17,7 +17,7 @@ export interface Props { sourceField: string; label: string; query?: string; - indexPatternTitle?: string; + dataViewTitle?: string; filters?: ESFilter[]; time?: { from: string; to: string }; keepHistory?: boolean; @@ -59,7 +59,7 @@ const getIncludeClause = (sourceField: string, query?: string) => { export const useValuesList = ({ sourceField, - indexPatternTitle, + dataViewTitle, query = '', filters, time, @@ -91,7 +91,7 @@ export const useValuesList = ({ const { data, loading } = useEsSearch( createEsParams({ - index: indexPatternTitle!, + index: dataViewTitle!, body: { query: { bool: { @@ -135,7 +135,7 @@ export const useValuesList = ({ }, }, }), - [debouncedQuery, from, to, JSON.stringify(filters), indexPatternTitle, sourceField], + [debouncedQuery, from, to, JSON.stringify(filters), dataViewTitle, sourceField], { name: `get${label.replace(/\s/g, '')}ValuesList` } ); diff --git a/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.test.ts b/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.test.ts index 0a24f35a498cc..eb7df98da599b 100644 --- a/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.test.ts +++ b/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.test.ts @@ -6,7 +6,7 @@ */ import { dataViewList, ObservabilityDataViews } from './observability_data_views'; -import { mockCore, mockIndexPattern } from '../../components/shared/exploratory_view/rtl_helpers'; +import { mockCore, mockDataView } from '../../components/shared/exploratory_view/rtl_helpers'; import { SavedObjectNotFound } from '../../../../../../src/plugins/kibana_utils/public'; const fieldFormats = { @@ -116,11 +116,11 @@ describe('ObservabilityIndexPatterns', function () { }); it('should validate field formats', async function () { - mockIndexPattern.getFormatterForField = jest.fn().mockReturnValue({ params: () => {} }); + mockDataView.getFormatterForField = jest.fn().mockReturnValue({ params: () => {} }); const obsv = new ObservabilityDataViews(dataViews!); - await obsv.validateFieldFormats('ux', mockIndexPattern); + await obsv.validateFieldFormats('ux', mockDataView); expect(dataViews?.updateSavedObject).toHaveBeenCalledTimes(1); expect(dataViews?.updateSavedObject).toHaveBeenCalledWith( diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx index e10e9267df184..0e083d1fcd679 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx @@ -134,7 +134,7 @@ export const FiltersExpressionsSelect: React.FC = { diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx index 06c080cc659fc..e25e764390f6b 100644 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx @@ -57,7 +57,7 @@ export const FilterGroup = () => { { ...selectedItems.map((value) => ( { onChange( field, @@ -51,7 +51,7 @@ export const SelectedFilters = ({ onChange }: Props) => { ...excludedItems.map((value) => ( { onChange( field, diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/index.tsx b/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/index.tsx index b472258704e5d..766e4d6f5baa5 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/index.tsx +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/index.tsx @@ -110,7 +110,7 @@ function LocalUIFilters() { ( { onChange( name, diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx b/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx index 8a104b1ae9bd0..46257605a22ce 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/local_uifilters/selected_wildcards.tsx @@ -47,7 +47,7 @@ export function SelectedWildcards({ indexPattern }: Props) { return searchTerm ? ( { updateSearchTerm(''); }} From b32f1daa86ae46f27e36079786273258540cc57a Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 9 Mar 2022 13:30:27 +0200 Subject: [PATCH 105/140] [Cloud Posture] Update resource type field query #127256 --- .../server/routes/compliance_dashboard/get_resources_types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts index 459dce56042da..0fc6e4b00944a 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_resources_types.ts @@ -31,7 +31,7 @@ export interface ResourceTypeBucket extends KeyDocCount { export const resourceTypeAggQuery = { aggs_by_resource_type: { terms: { - field: 'resource.type.keyword', + field: 'type.keyword', }, aggs: { failed_findings: { From b20a3642a6fed24e5d1c94e30e6bd2565a9f7e58 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Wed, 9 Mar 2022 12:54:29 +0100 Subject: [PATCH 106/140] [Cases] Remove temporal public methods from the cases plugin (#127248) --- x-pack/plugins/cases/public/mocks.ts | 2 -- x-pack/plugins/cases/public/plugin.ts | 8 ++------ x-pack/plugins/cases/public/types.ts | 7 ------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/cases/public/mocks.ts b/x-pack/plugins/cases/public/mocks.ts index a3876e9e19322..f3734be220da0 100644 --- a/x-pack/plugins/cases/public/mocks.ts +++ b/x-pack/plugins/cases/public/mocks.ts @@ -13,10 +13,8 @@ export const mockCasesContract = (): jest.Mocked => ({ getCases: jest.fn(), getCasesContext: jest.fn().mockImplementation(() => mockCasesContext), getAllCasesSelectorModal: jest.fn(), - getAllCasesSelectorModalNoProvider: jest.fn(), getCreateCaseFlyout: jest.fn(), getRecentCases: jest.fn(), - getCreateCaseFlyoutNoProvider: jest.fn(), hooks: { getUseCasesAddToNewCaseFlyout: jest.fn(), getUseCasesAddToExistingCaseModal: jest.fn(), diff --git a/x-pack/plugins/cases/public/plugin.ts b/x-pack/plugins/cases/public/plugin.ts index ae1d87d9966a2..6b1e7b784b630 100644 --- a/x-pack/plugins/cases/public/plugin.ts +++ b/x-pack/plugins/cases/public/plugin.ts @@ -14,8 +14,6 @@ import { getAllCasesSelectorModalLazy, getCreateCaseFlyoutLazy, canUseCases, - getCreateCaseFlyoutLazyNoProvider, - getAllCasesSelectorModalNoProviderLazy, } from './methods'; import { CasesUiConfigType } from '../common/ui/types'; import { APP_ID, APP_PATH } from '../common/constants'; @@ -93,12 +91,10 @@ export class CasesUiPlugin getCases: getCasesLazy, getCasesContext: getCasesContextLazy, getRecentCases: getRecentCasesLazy, + // @deprecated Please use the hook getUseCasesAddToNewCaseFlyout getCreateCaseFlyout: getCreateCaseFlyoutLazy, + // @deprecated Please use the hook getUseCasesAddToExistingCaseModal getAllCasesSelectorModal: getAllCasesSelectorModalLazy, - // Temporal methods to remove timelines and cases deep integration - // https://github.com/elastic/kibana/issues/123183 - getCreateCaseFlyoutNoProvider: getCreateCaseFlyoutLazyNoProvider, - getAllCasesSelectorModalNoProvider: getAllCasesSelectorModalNoProviderLazy, hooks: { getUseCasesAddToNewCaseFlyout: useCasesAddToNewCaseFlyout, getUseCasesAddToExistingCaseModal: useCasesAddToExistingCaseModal, diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index c756a5f73a0a7..b136892717135 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -22,7 +22,6 @@ import type { SpacesPluginStart } from '../../spaces/public'; import type { TriggersAndActionsUIPublicPluginStart as TriggersActionsStart } from '../../triggers_actions_ui/public'; import { CommentRequestAlertType, CommentRequestUserType } from '../common/api'; import { UseCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal'; -import { CreateCaseFlyoutProps } from './components/create/flyout'; import { UseCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout'; import type { @@ -97,18 +96,12 @@ export interface CasesUiStart { getAllCasesSelectorModal: ( props: GetAllCasesSelectorModalProps ) => ReactElement; - getAllCasesSelectorModalNoProvider: ( - props: GetAllCasesSelectorModalProps - ) => ReactElement; /** * Flyout with the form to create a case for the owner * @param props GetCreateCaseFlyoutProps * @returns A react component that is a flyout for creating a case */ getCreateCaseFlyout: (props: GetCreateCaseFlyoutProps) => ReactElement; - getCreateCaseFlyoutNoProvider: ( - props: CreateCaseFlyoutProps - ) => ReactElement; /** * Get the recent cases component * @param props GetRecentCasesProps From 8dcf1ad2ed165cc7de46c667dc54fb6397ad15d8 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 9 Mar 2022 14:29:35 +0200 Subject: [PATCH 107/140] [TSVB2Lens] Supports static values (#126903) * [TSVB2Lens] Support static values * Fixes TSVB aggregations * Adds unit tests * Add more unit tests * Cleanup --- .../public/trigger_action/get_series.test.ts | 59 +++++++++++++++---- .../public/trigger_action/get_series.ts | 31 ++++++++-- .../timeseries/public/trigger_action/index.ts | 7 ++- .../public/trigger_action/metrics_helpers.ts | 7 ++- .../trigger_action/supported_metrics.ts | 4 ++ .../editor_frame/suggestion_helpers.ts | 7 ++- .../indexpattern_suggestions.test.tsx | 55 +++++++++++++++++ .../indexpattern_suggestions.ts | 4 ++ .../operations/layer_helpers.ts | 11 ++-- x-pack/plugins/lens/public/types.ts | 1 + .../xy_visualization/visualization.test.ts | 43 ++++++++++++++ .../public/xy_visualization/visualization.tsx | 4 ++ .../public/xy_visualization/xy_suggestions.ts | 3 +- 13 files changed, 204 insertions(+), 32 deletions(-) diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_series.test.ts b/src/plugins/vis_types/timeseries/public/trigger_action/get_series.test.ts index 3b01aeebb2dcf..31c8072486615 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_series.test.ts +++ b/src/plugins/vis_types/timeseries/public/trigger_action/get_series.test.ts @@ -17,7 +17,7 @@ describe('getSeries', () => { field: 'day_of_week_i', }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'average', @@ -44,7 +44,7 @@ describe('getSeries', () => { }, }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'formula', @@ -71,7 +71,7 @@ describe('getSeries', () => { field: '123456', }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'formula', @@ -97,7 +97,7 @@ describe('getSeries', () => { field: '123456', }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'formula', @@ -122,7 +122,7 @@ describe('getSeries', () => { field: '123456', }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'cumulative_sum', @@ -147,7 +147,7 @@ describe('getSeries', () => { field: '123456', }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'formula', @@ -174,7 +174,7 @@ describe('getSeries', () => { unit: '1m', }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'differences', @@ -202,7 +202,7 @@ describe('getSeries', () => { window: 6, }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'moving_average', @@ -246,7 +246,7 @@ describe('getSeries', () => { window: 6, }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'formula', @@ -293,7 +293,7 @@ describe('getSeries', () => { ], }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'percentile', @@ -335,7 +335,7 @@ describe('getSeries', () => { order_by: 'timestamp', }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'last_value', @@ -357,10 +357,43 @@ describe('getSeries', () => { size: 2, }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toBeNull(); }); + test('should return null for a static aggregation with 1 layer', () => { + const metric = [ + { + id: '12345', + type: 'static', + value: '10', + }, + ] as Metric[]; + const config = getSeries(metric, 1); + expect(config).toBeNull(); + }); + + test('should return the correct config for a static aggregation with 2 layers', () => { + const metric = [ + { + id: '12345', + type: 'static', + value: '10', + }, + ] as Metric[]; + const config = getSeries(metric, 2); + expect(config).toStrictEqual([ + { + agg: 'static_value', + fieldName: 'document', + isFullReference: true, + params: { + value: '10', + }, + }, + ]); + }); + test('should return the correct formula for the math aggregation with percentiles as variables', () => { const metric = [ { @@ -415,7 +448,7 @@ describe('getSeries', () => { ], }, ] as Metric[]; - const config = getSeries(metric); + const config = getSeries(metric, 1); expect(config).toStrictEqual([ { agg: 'formula', diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/get_series.ts b/src/plugins/vis_types/timeseries/public/trigger_action/get_series.ts index 5e7d39f3085f6..641890b68b83c 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/get_series.ts +++ b/src/plugins/vis_types/timeseries/public/trigger_action/get_series.ts @@ -21,7 +21,10 @@ import { getTimeScale, } from './metrics_helpers'; -export const getSeries = (metrics: Metric[]): VisualizeEditorLayersContext['metrics'] | null => { +export const getSeries = ( + metrics: Metric[], + totalSeriesNum: number +): VisualizeEditorLayersContext['metrics'] | null => { const metricIdx = metrics.length - 1; const aggregation = metrics[metricIdx].type; const fieldName = metrics[metricIdx].field; @@ -50,12 +53,10 @@ export const getSeries = (metrics: Metric[]): VisualizeEditorLayersContext['metr const variables = metrics[mathMetricIdx].variables; const layerMetricsArray = metrics; if (!finalScript || !variables) return null; + const metricsWithoutMath = layerMetricsArray.filter((metric) => metric.type !== 'math'); // create the script - for (let layerMetricIdx = 0; layerMetricIdx < layerMetricsArray.length; layerMetricIdx++) { - if (layerMetricsArray[layerMetricIdx].type === 'math') { - continue; - } + for (let layerMetricIdx = 0; layerMetricIdx < metricsWithoutMath.length; layerMetricIdx++) { const currentMetric = metrics[layerMetricIdx]; // We can only support top_hit with size 1 if ( @@ -102,7 +103,7 @@ export const getSeries = (metrics: Metric[]): VisualizeEditorLayersContext['metr // percentile value is derived from the field Id. It has the format xxx-xxx-xxx-xxx[percentile] const [fieldId, meta] = metrics[metricIdx]?.field?.split('[') ?? []; const subFunctionMetric = metrics.find((metric) => metric.id === fieldId); - if (!subFunctionMetric) { + if (!subFunctionMetric || subFunctionMetric.type === 'static') { return null; } const pipelineAgg = getPipelineAgg(subFunctionMetric); @@ -184,6 +185,24 @@ export const getSeries = (metrics: Metric[]): VisualizeEditorLayersContext['metr ]; break; } + case 'static': { + // Lens support reference lines only when at least one layer data exists + if (totalSeriesNum === 1) { + return null; + } + const staticValue = metrics[metricIdx].value; + metricsArray = [ + { + agg: aggregationMap.name, + isFullReference: aggregationMap.isFullReference, + fieldName: 'document', + params: { + ...(staticValue && { value: staticValue }), + }, + }, + ]; + break; + } default: { const timeScale = getTimeScale(metrics[metricIdx]); metricsArray = [ diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/index.ts b/src/plugins/vis_types/timeseries/public/trigger_action/index.ts index 8e757b98c125d..286b1eaf4725c 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/index.ts +++ b/src/plugins/vis_types/timeseries/public/trigger_action/index.ts @@ -37,6 +37,11 @@ export const triggerTSVBtoLensConfiguration = async ( return null; } const layersConfiguration: { [key: string]: VisualizeEditorLayersContext } = {}; + // get the active series number + let seriesNum = 0; + model.series.forEach((series) => { + if (!series.hidden) seriesNum++; + }); // handle multiple layers/series for (let layerIdx = 0; layerIdx < model.series.length; layerIdx++) { @@ -64,7 +69,7 @@ export const triggerTSVBtoLensConfiguration = async ( } // handle multiple metrics - let metricsArray = getSeries(layer.metrics); + let metricsArray = getSeries(layer.metrics, seriesNum); if (!metricsArray) { return null; } diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.ts b/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.ts index dc9457ac1fafc..a71f43f2a6a25 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.ts +++ b/src/plugins/vis_types/timeseries/public/trigger_action/metrics_helpers.ts @@ -91,7 +91,7 @@ export const getParentPipelineSeries = ( // percentile value is derived from the field Id. It has the format xxx-xxx-xxx-xxx[percentile] const [fieldId, meta] = currentMetric?.field?.split('[') ?? []; const subFunctionMetric = metrics.find((metric) => metric.id === fieldId); - if (!subFunctionMetric) { + if (!subFunctionMetric || subFunctionMetric.type === 'static') { return null; } const pipelineAgg = getPipelineAgg(subFunctionMetric); @@ -184,7 +184,7 @@ export const getSiblingPipelineSeriesFormula = ( metrics: Metric[] ) => { const subFunctionMetric = metrics.find((metric) => metric.id === currentMetric.field); - if (!subFunctionMetric) { + if (!subFunctionMetric || subFunctionMetric.type === 'static') { return null; } const pipelineAggMap = SUPPORTED_METRICS[subFunctionMetric.type]; @@ -311,6 +311,9 @@ export const getFormulaEquivalent = ( case 'filter_ratio': { return getFilterRatioFormula(currentMetric); } + case 'static': { + return `${currentMetric.value}`; + } default: { return `${aggregation}(${currentMetric.field})`; } diff --git a/src/plugins/vis_types/timeseries/public/trigger_action/supported_metrics.ts b/src/plugins/vis_types/timeseries/public/trigger_action/supported_metrics.ts index 354b60c31854a..3a304590b642d 100644 --- a/src/plugins/vis_types/timeseries/public/trigger_action/supported_metrics.ts +++ b/src/plugins/vis_types/timeseries/public/trigger_action/supported_metrics.ts @@ -92,4 +92,8 @@ export const SUPPORTED_METRICS: { [key: string]: AggOptions } = { name: 'clamp', isFullReference: true, }, + static: { + name: 'static_value', + isFullReference: true, + }, }; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts index c9d237961b475..0dddf982bcbc2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts @@ -148,7 +148,8 @@ export function getSuggestions({ }, currentVisualizationState, subVisualizationId, - palette + palette, + visualizeTriggerFieldContext && 'isVisualizeAction' in visualizeTriggerFieldContext ); }); }) @@ -205,7 +206,8 @@ function getVisualizationSuggestions( datasourceSuggestion: DatasourceSuggestion & { datasourceId: string }, currentVisualizationState: unknown, subVisualizationId?: string, - mainPalette?: PaletteOutput + mainPalette?: PaletteOutput, + isFromContext?: boolean ) { return visualization .getSuggestions({ @@ -214,6 +216,7 @@ function getVisualizationSuggestions( keptLayerIds: datasourceSuggestion.keptLayerIds, subVisualizationId, mainPalette, + isFromContext, }) .map(({ state, ...visualizationSuggestion }) => ({ ...visualizationSuggestion, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 8c8136371b189..cca739c949d4f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -1835,6 +1835,61 @@ describe('IndexPattern Data Source suggestions', () => { }) ); }); + + it('should apply a static layer if it is provided', () => { + const updatedContext = [ + { + ...context[0], + metrics: [ + { + agg: 'static_value', + isFullReference: true, + fieldName: 'document', + params: { + value: '10', + }, + color: '#68BC00', + }, + ], + }, + ]; + const suggestions = getDatasourceSuggestionsForVisualizeCharts( + stateWithoutLayer(), + updatedContext + ); + + expect(suggestions).toContainEqual( + expect.objectContaining({ + state: expect.objectContaining({ + layers: { + id1: expect.objectContaining({ + columnOrder: ['id2'], + columns: { + id2: expect.objectContaining({ + operationType: 'static_value', + isStaticValue: true, + params: expect.objectContaining({ + value: '10', + }), + }), + }, + }), + }, + }), + table: { + changeType: 'initial', + label: undefined, + isMultiRow: false, + columns: [ + expect.objectContaining({ + columnId: 'id2', + }), + ], + layerId: 'id1', + }, + }) + ); + }); }); describe('#getDatasourceSuggestionsForVisualizeField', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index 8e4017a583a91..d13aeced2c4af 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -203,6 +203,10 @@ function createNewTimeseriesLayerWithMetricAggregationFromVizEditor( layer.format, layer.label ); + // static values layers do not need a date histogram column + if (Object.values(computedLayer.columns)[0].isStaticValue) { + return computedLayer; + } return insertNewColumn({ op: 'date_histogram', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 2252c5b38a541..0c4428b54b673 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -228,15 +228,12 @@ export function insertNewColumn({ const possibleOperation = operationDefinition.getPossibleOperation(); const isBucketed = Boolean(possibleOperation?.isBucketed); const addOperationFn = isBucketed ? addBucket : addMetric; + const buildColumnFn = columnParams + ? operationDefinition.buildColumn({ ...baseOptions, layer }, columnParams) + : operationDefinition.buildColumn({ ...baseOptions, layer }); return updateDefaultLabels( - addOperationFn( - layer, - operationDefinition.buildColumn({ ...baseOptions, layer }), - columnId, - visualizationGroups, - targetGroup - ), + addOperationFn(layer, buildColumnFn, columnId, visualizationGroups, targetGroup), indexPattern ); } diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 1895d26ea89f5..107884a849217 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -634,6 +634,7 @@ export interface SuggestionRequest { */ state?: T; mainPalette?: PaletteOutput; + isFromContext?: boolean; /** * The visualization needs to know which table is being suggested */ diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 89b496a785d9f..5b430fd7fc579 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -435,6 +435,49 @@ describe('xy_visualization', () => { type: 'palette', }); }); + + it('sets the context configuration correctly for reference lines', () => { + const newContext = { + ...context, + metrics: [ + { + agg: 'static_value', + fieldName: 'document', + isFullReference: true, + color: '#68BC00', + params: { + value: '10', + }, + }, + ], + }; + const state = xyVisualization?.updateLayersConfigurationFromContext?.({ + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + seriesType: 'line', + xAccessor: undefined, + accessors: ['a'], + }, + ], + }, + layerId: 'first', + context: newContext, + }); + expect(state?.layers[0]).toHaveProperty('seriesType', 'area'); + expect(state?.layers[0]).toHaveProperty('layerType', 'referenceLine'); + expect(state?.layers[0].yConfig).toStrictEqual([ + { + axisMode: 'right', + color: '#68BC00', + forAccessor: 'a', + fill: 'below', + }, + ]); + }); }); describe('#getVisualizationSuggestionFromContext', () => { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 33b285bd19ea3..17f397980b621 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -15,6 +15,7 @@ import { FieldFormatsStart } from 'src/plugins/field_formats/public'; import { ThemeServiceStart } from 'kibana/public'; import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; +import type { FillStyle } from '../../common/expressions/xy_chart'; import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; @@ -290,12 +291,14 @@ export const getXyVisualization = ({ if (!foundLayer) { return prevState; } + const isReferenceLine = metrics.some((metric) => metric.agg === 'static_value'); const axisMode = axisPosition as YAxisMode; const yConfig = metrics.map((metric, idx) => { return { color: metric.color, forAccessor: metric.accessor ?? foundLayer.accessors[idx], ...(axisMode && { axisMode }), + ...(isReferenceLine && { fill: chartType === 'area' ? 'below' : ('none' as FillStyle) }), }; }); const newLayer = { @@ -303,6 +306,7 @@ export const getXyVisualization = ({ ...(chartType && { seriesType: chartType as SeriesType }), ...(palette && { palette }), yConfig, + layerType: isReferenceLine ? layerTypes.REFERENCELINE : layerTypes.DATA, }; const newLayers = prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index 656c3fa8422e5..1578442b52815 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -45,6 +45,7 @@ export function getSuggestions({ keptLayerIds, subVisualizationId, mainPalette, + isFromContext, }: SuggestionRequest): Array> { const incompleteTable = !table.isMultiRow || @@ -72,7 +73,7 @@ export function getSuggestions({ if ( (incompleteTable && state && !subVisualizationId) || - table.columns.some((col) => col.operation.isStaticValue) || + table.columns.some((col) => col.operation.isStaticValue && !isFromContext) || // do not use suggestions with non-numeric metrics table.columns.some((col) => !col.operation.isBucketed && col.operation.dataType !== 'number') ) { From c149966af534996865f5554e975e8966a2b6bf61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Wed, 9 Mar 2022 13:48:04 +0100 Subject: [PATCH 108/140] Add instructions for connecting to local ES cluster started with `yarn es snapshot` (#127262) --- x-pack/plugins/apm/dev_docs/local_setup.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugins/apm/dev_docs/local_setup.md b/x-pack/plugins/apm/dev_docs/local_setup.md index b3c9d0acbaebc..f021f41b17c80 100644 --- a/x-pack/plugins/apm/dev_docs/local_setup.md +++ b/x-pack/plugins/apm/dev_docs/local_setup.md @@ -32,6 +32,15 @@ node ./scripts/es_archiver load "x-pack/plugins/apm/ftr_e2e/cypress/fixtures/es_ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts --target=http://elastic:changeme@localhost:9200 ``` +**Connect Kibana to ES** +Update `config/kibana.dev.yml` with: + +```yml +elasticsearch.hosts: http://localhost:9200 +elasticsearch.username: kibana_system +elasticsearch.password: changeme +``` + Documentation for [Synthtrace](https://github.com/elastic/kibana/blob/main/packages/elastic-apm-synthtrace/README.md) ## 2. Cloud-based ES Cluster (internal devs only) From 671960f1e63f8d3e006eb9fb7f88bd6705810a87 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Wed, 9 Mar 2022 15:02:45 +0200 Subject: [PATCH 109/140] [Cloud Posture] Add rules page (#126990) --- .../common/utils/helpers.ts | 3 +- .../public/common/navigation/constants.ts | 2 +- .../public/pages/rules/index.tsx | 13 +- .../public/pages/rules/rules_bottom_bar.tsx | 40 +++ .../pages/rules/rules_bulk_actions_menu.tsx | 53 +++ .../pages/rules/rules_container.test.tsx | 339 ++++++++++++++++++ .../public/pages/rules/rules_container.tsx | 188 ++++++++++ .../public/pages/rules/rules_table.tsx | 135 +++++++ .../public/pages/rules/rules_table_header.tsx | 224 ++++++++++++ .../public/pages/rules/test_subjects.ts | 20 ++ .../public/pages/rules/translations.ts | 64 ++++ .../public/pages/rules/use_csp_rules.ts | 44 +-- 12 files changed, 1099 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bulk_actions_menu.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/rules/test_subjects.ts create mode 100644 x-pack/plugins/cloud_security_posture/public/pages/rules/translations.ts diff --git a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts index 1df0c18ebdd02..745b59724dcb2 100644 --- a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts +++ b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import * as t from 'io-ts'; /** * @example @@ -16,7 +15,7 @@ export const isNonNullable = (v: T): v is NonNullable => export const extractErrorMessage = (e: unknown, defaultMessage = 'Unknown Error'): string => { if (e instanceof Error) return e.message; - if (t.record(t.literal('message'), t.string).is(e)) return e.message; + if (typeof e === 'string') return e; return defaultMessage; // TODO: i18n }; diff --git a/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts index 4e07a4c800f53..8603e13159f5b 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts @@ -12,7 +12,7 @@ import type { CspPage, CspNavigationItem } from './types'; export const allNavigationItems: Record = { dashboard: { name: TEXT.DASHBOARD, path: '/dashboard' }, findings: { name: TEXT.FINDINGS, path: '/findings' }, - rules: { name: 'Rules', path: '/rules', disabled: true }, + rules: { name: 'Rules', path: '/rules', disabled: !INTERNAL_FEATURE_FLAGS.showBenchmarks }, benchmarks: { name: TEXT.MY_BENCHMARKS, path: '/benchmarks', diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx index 130f03fd5784d..0b511c9fb9031 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx @@ -7,6 +7,9 @@ import React from 'react'; import type { EuiPageHeaderProps } from '@elastic/eui'; import { CspPageTemplate } from '../../components/page_template'; +import { RulesContainer } from './rules_container'; +import { allNavigationItems } from '../../common/navigation/constants'; +import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; // TODO: // - get selected integration @@ -15,6 +18,14 @@ const pageHeader: EuiPageHeaderProps = { pageTitle: 'Rules', }; +const breadcrumbs = [allNavigationItems.rules]; + export const Rules = () => { - return ; + useCspBreadcrumbs(breadcrumbs); + + return ( + + + + ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx new file mode 100644 index 0000000000000..ebf4913f895c3 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bottom_bar.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiBottomBar, EuiButton } from '@elastic/eui'; +import * as TEST_SUBJECTS from './test_subjects'; +import * as TEXT from './translations'; + +interface RulesBottomBarProps { + onSave(): void; + onCancel(): void; + isLoading: boolean; +} + +export const RulesBottomBar = ({ onSave, onCancel, isLoading }: RulesBottomBarProps) => ( + + + + + {TEXT.CANCEL} + + + + + {TEXT.SAVE} + + + + +); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bulk_actions_menu.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bulk_actions_menu.tsx new file mode 100644 index 0000000000000..1f264fd33e1c0 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_bulk_actions_menu.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useState } from 'react'; +import { EuiContextMenuPanel, EuiContextMenuItem, EuiPopover, EuiButtonEmpty } from '@elastic/eui'; +import * as TEST_SUBJECTS from './test_subjects'; +import * as TEXT from './translations'; + +interface RulesBulkActionsMenuProps { + items: ReadonlyArray>; +} + +export const RulesBulkActionsMenu = ({ items }: RulesBulkActionsMenuProps) => { + const [isPopoverOpen, setPopover] = useState(false); + const onButtonClick = () => setPopover(!isPopoverOpen); + const closePopover = () => setPopover(false); + + const panelItems = items.map((item, i) => ( + { + closePopover(); + item.onClick?.(e); + }} + /> + )); + + const button = ( + + {TEXT.BULK_ACTIONS} + + ); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx new file mode 100644 index 0000000000000..bcbc4d5c4bd44 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx @@ -0,0 +1,339 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { RulesContainer } from './rules_container'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { QueryClient } from 'react-query'; +import { useFindCspRules, useBulkUpdateCspRules, type RuleSavedObject } from './use_csp_rules'; +import * as TEST_SUBJECTS from './test_subjects'; +import { Chance } from 'chance'; +import { TestProvider } from '../../test/test_provider'; + +const chance = new Chance(); + +jest.mock('./use_csp_rules', () => ({ + useFindCspRules: jest.fn(), + useBulkUpdateCspRules: jest.fn(), +})); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + }, +}); + +const getWrapper = + (): React.FC => + ({ children }) => + {children}; + +const getRuleMock = ({ id = chance.guid(), enabled }: { id?: string; enabled: boolean }) => + ({ + id, + updatedAt: chance.date().toISOString(), + attributes: { + id, + name: chance.word(), + enabled, + }, + } as RuleSavedObject); + +describe('', () => { + beforeEach(() => { + queryClient.clear(); + jest.clearAllMocks(); + (useBulkUpdateCspRules as jest.Mock).mockReturnValue({ + status: 'idle', + mutate: jest.fn(), + }); + }); + + it('displays rules with their initial state', async () => { + const Wrapper = getWrapper(); + const rule1 = getRuleMock({ enabled: true }); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: 1, + savedObjects: [rule1], + }, + }); + + render( + + + + ); + + expect(await screen.findByTestId(TEST_SUBJECTS.CSP_RULES_CONTAINER)).toBeInTheDocument(); + expect(await screen.findByText(rule1.attributes.name)).toBeInTheDocument(); + expect( + screen + .getByTestId(TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule1.id)) + .getAttribute('aria-checked') + ).toEqual('true'); + }); + + it('toggles rules locally', () => { + const Wrapper = getWrapper(); + const rule1 = getRuleMock({ enabled: false }); + const rule2 = getRuleMock({ enabled: true }); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: 2, + savedObjects: [rule1, rule2], + }, + }); + + render( + + + + ); + + const switchId1 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule1.id); + const switchId2 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule2.id); + + fireEvent.click(screen.getByTestId(switchId1)); + fireEvent.click(screen.getByTestId(switchId2)); + + expect(screen.getByTestId(switchId1).getAttribute('aria-checked')).toEqual( + (!rule1.attributes.enabled).toString() + ); + expect(screen.getByTestId(switchId2).getAttribute('aria-checked')).toEqual( + (!rule2.attributes.enabled).toString() + ); + }); + + it('bulk toggles rules locally', () => { + const Wrapper = getWrapper(); + const rule1 = getRuleMock({ enabled: true }); + const rule2 = getRuleMock({ enabled: true }); + const rule3 = getRuleMock({ enabled: false }); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: 3, + savedObjects: [rule1, rule2, rule3], + }, + }); + + render( + + + + ); + + const switchId1 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule1.id); + const switchId2 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule2.id); + const switchId3 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule3.id); + + fireEvent.click(screen.getByTestId('checkboxSelectAll')); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_MENU_BUTTON)); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_DISABLE_BUTTON)); + + expect(screen.getByTestId(switchId1).getAttribute('aria-checked')).toEqual( + (!rule1.attributes.enabled).toString() + ); + expect(screen.getByTestId(switchId2).getAttribute('aria-checked')).toEqual( + (!rule2.attributes.enabled).toString() + ); + expect(screen.getByTestId(switchId3).getAttribute('aria-checked')).toEqual( + rule3.attributes.enabled.toString() + ); + }); + + it('updates rules with local changes done by non-bulk toggles', () => { + const Wrapper = getWrapper(); + const rule1 = getRuleMock({ enabled: false }); + const rule2 = getRuleMock({ enabled: true }); + const rule3 = getRuleMock({ enabled: true }); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: 3, + savedObjects: [rule1, rule2, rule3], + }, + }); + + render( + + + + ); + const { mutate } = useBulkUpdateCspRules(); + + const switchId1 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule1.id); + const switchId2 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule2.id); + const switchId3 = TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule3.id); + + fireEvent.click(screen.getByTestId(switchId1)); + fireEvent.click(screen.getByTestId(switchId2)); + fireEvent.click(screen.getByTestId(switchId3)); // adds + fireEvent.click(screen.getByTestId(switchId3)); // removes + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); + + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith([ + { ...rule1.attributes, enabled: !rule1.attributes.enabled }, + { ...rule2.attributes, enabled: !rule2.attributes.enabled }, + ]); + }); + + it('updates rules with local changes done by bulk toggles', () => { + const Wrapper = getWrapper(); + const rule1 = getRuleMock({ enabled: false }); + const rule2 = getRuleMock({ enabled: true }); + const rule3 = getRuleMock({ enabled: true }); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: 3, + savedObjects: [rule1, rule2, rule3], + }, + }); + + render( + + + + ); + const { mutate } = useBulkUpdateCspRules(); + + fireEvent.click(screen.getByTestId('checkboxSelectAll')); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_MENU_BUTTON)); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_ENABLE_BUTTON)); // This should only change rule1 + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); + + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith([ + { ...rule1.attributes, enabled: !rule1.attributes.enabled }, + ]); + }); + + it('only changes selected rules in bulk operations', () => { + const Wrapper = getWrapper(); + const rule1 = getRuleMock({ enabled: false }); + const rule2 = getRuleMock({ enabled: true }); + const rule3 = getRuleMock({ enabled: false }); + const rule4 = getRuleMock({ enabled: false }); + const rule5 = getRuleMock({ enabled: true }); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: 4, + savedObjects: [rule1, rule2, rule3, rule4, rule5], + }, + }); + + render( + + + + ); + const { mutate } = useBulkUpdateCspRules(); + + fireEvent.click(screen.getByTestId(`checkboxSelectRow-${rule1.id}`)); // changes + fireEvent.click(screen.getByTestId(`checkboxSelectRow-${rule2.id}`)); // doesn't change + fireEvent.click(screen.getByTestId(`checkboxSelectRow-${rule4.id}`)); // changes + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_MENU_BUTTON)); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_ENABLE_BUTTON)); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule5.id))); // changes + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); + + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith([ + { ...rule1.attributes, enabled: !rule1.attributes.enabled }, + { ...rule4.attributes, enabled: !rule4.attributes.enabled }, + { ...rule5.attributes, enabled: !rule5.attributes.enabled }, + ]); + }); + + it('updates rules with changes of both bulk/non-bulk toggles', () => { + const Wrapper = getWrapper(); + const rule1 = getRuleMock({ enabled: false }); + const rule2 = getRuleMock({ enabled: true }); + const rule3 = getRuleMock({ enabled: false }); + const rule4 = getRuleMock({ enabled: false }); + const rule5 = getRuleMock({ enabled: true }); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: 4, + savedObjects: [rule1, rule2, rule3, rule4, rule5], + }, + }); + + render( + + + + ); + + const { mutate } = useBulkUpdateCspRules(); + + fireEvent.click(screen.getByTestId(`checkboxSelectRow-${rule1.id}`)); // changes rule1 + fireEvent.click(screen.getByTestId(`checkboxSelectRow-${rule2.id}`)); // doesn't change + fireEvent.click(screen.getByTestId(`checkboxSelectRow-${rule4.id}`)); // changes rule4 + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_MENU_BUTTON)); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_DISABLE_BUTTON)); // changes rule2 + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_ENABLE_BUTTON)); // reverts rule2 + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule5.id))); // changes rule5 + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); + + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith([ + { ...rule1.attributes, enabled: !rule1.attributes.enabled }, + { ...rule4.attributes, enabled: !rule4.attributes.enabled }, + { ...rule5.attributes, enabled: !rule5.attributes.enabled }, + ]); + }); + + it('selects and updates all rules', async () => { + const Wrapper = getWrapper(); + const enabled = true; + const rules = Array.from({ length: 20 }, () => getRuleMock({ enabled })); + + (useFindCspRules as jest.Mock).mockReturnValue({ + status: 'success', + data: { + total: rules.length, + savedObjects: rules, + }, + }); + + render( + + + + ); + + const { mutate } = useBulkUpdateCspRules(); + + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_SELECT_ALL_BUTTON)); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_MENU_BUTTON)); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_TABLE_BULK_DISABLE_BUTTON)); + fireEvent.click(screen.getByTestId(TEST_SUBJECTS.CSP_RULES_SAVE_BUTTON)); + + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith( + rules.map((rule) => ({ + ...rule.attributes, + enabled: !enabled, + })) + ); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx new file mode 100644 index 0000000000000..108d33cad3ebe --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.tsx @@ -0,0 +1,188 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useEffect, useState, useMemo, useCallback, useRef } from 'react'; +import { type EuiBasicTable, EuiPanel, EuiSpacer } from '@elastic/eui'; +import { extractErrorMessage } from '../../../common/utils/helpers'; +import { RulesTable } from './rules_table'; +import { RulesBottomBar } from './rules_bottom_bar'; +import { RulesTableHeader } from './rules_table_header'; +import { + useFindCspRules, + useBulkUpdateCspRules, + type RuleSavedObject, + type RulesQuery, + type RulesQueryResult, +} from './use_csp_rules'; +import * as TEST_SUBJECTS from './test_subjects'; + +interface RulesPageData { + rules_page: RuleSavedObject[]; + all_rules: RuleSavedObject[]; + rules_map: Map; + total: number; + error?: string; + loading: boolean; +} + +export type RulesState = RulesPageData & RulesQuery; + +const getSimpleQueryString = (searchValue?: string): string => + searchValue ? `${searchValue}*` : ''; + +const getChangedRules = ( + baseRules: ReadonlyMap, + currentChangedRules: ReadonlyMap, + rulesToChange: readonly RuleSavedObject[] +): Map => { + const changedRules = new Map(currentChangedRules); + + rulesToChange.forEach((ruleToChange) => { + const baseRule = baseRules.get(ruleToChange.id); + const changedRule = changedRules.get(ruleToChange.id); + + if (!baseRule) throw new Error('expected base rule to exists'); + + const baseRuleChanged = baseRule.attributes.enabled !== ruleToChange.attributes.enabled; + + if (!changedRule && baseRuleChanged) changedRules.set(ruleToChange.id, ruleToChange); + + if (changedRule && !baseRuleChanged) changedRules.delete(ruleToChange.id); + }); + + return changedRules; +}; + +const getRulesPageData = ( + { status, data, error }: Pick, + changedRules: Map, + query: RulesQuery +): RulesPageData => { + const rules = data?.savedObjects || []; + const page = getPage(rules, query); + return { + loading: status === 'loading', + error: error ? extractErrorMessage(error) : undefined, + all_rules: rules, + rules_map: new Map(rules.map((rule) => [rule.id, rule])), + rules_page: page.map((rule) => changedRules.get(rule.attributes.id) || rule), + total: data?.total || 0, + }; +}; + +const getPage = (data: readonly RuleSavedObject[], { page, perPage }: RulesQuery) => + data.slice(page * perPage, (page + 1) * perPage); + +const MAX_ITEMS_PER_PAGE = 10000; + +export const RulesContainer = () => { + const tableRef = useRef(null); + const [changedRules, setChangedRules] = useState>(new Map()); + const [isAllSelected, setIsAllSelected] = useState(false); + const [visibleSelectedRulesIds, setVisibleSelectedRulesIds] = useState([]); + const [rulesQuery, setRulesQuery] = useState({ page: 0, perPage: 5, search: '' }); + + const { data, status, error, refetch } = useFindCspRules({ + search: getSimpleQueryString(rulesQuery.search), + page: 1, + perPage: MAX_ITEMS_PER_PAGE, + }); + + const { mutate: bulkUpdate, isLoading: isUpdating } = useBulkUpdateCspRules(); + + const rulesPageData = useMemo( + () => getRulesPageData({ data, error, status }, changedRules, rulesQuery), + [data, error, status, changedRules, rulesQuery] + ); + + const hasChanges = !!changedRules.size; + + const selectAll = () => { + if (!tableRef.current) return; + tableRef.current.setSelection(rulesPageData.rules_page); + setIsAllSelected(true); + }; + + const toggleRules = (rules: RuleSavedObject[], enabled: boolean) => + setChangedRules( + getChangedRules( + rulesPageData.rules_map, + changedRules, + rules.map((rule) => ({ + ...rule, + attributes: { ...rule.attributes, enabled }, + })) + ) + ); + + const bulkToggleRules = (enabled: boolean) => + toggleRules( + isAllSelected + ? rulesPageData.all_rules + : visibleSelectedRulesIds.map((ruleId) => rulesPageData.rules_map.get(ruleId)!), + enabled + ); + + const toggleRule = (rule: RuleSavedObject) => toggleRules([rule], !rule.attributes.enabled); + + const bulkUpdateRules = () => bulkUpdate([...changedRules].map(([, rule]) => rule.attributes)); + + const discardChanges = useCallback(() => setChangedRules(new Map()), []); + + const clearSelection = useCallback(() => { + if (!tableRef.current) return; + tableRef.current.setSelection([]); + setIsAllSelected(false); + }, []); + + useEffect(discardChanges, [data, discardChanges]); + useEffect(clearSelection, [rulesQuery, clearSelection]); + + return ( +
+ + setRulesQuery((currentQuery) => ({ ...currentQuery, search: value }))} + refresh={() => { + clearSelection(); + refetch(); + }} + bulkEnable={() => bulkToggleRules(true)} + bulkDisable={() => bulkToggleRules(false)} + selectAll={selectAll} + clearSelection={clearSelection} + selectedRulesCount={ + isAllSelected ? rulesPageData.all_rules.length : visibleSelectedRulesIds.length + } + searchValue={rulesQuery.search} + totalRulesCount={rulesPageData.all_rules.length} + isSearching={status === 'loading'} + /> + + { + setIsAllSelected(false); + setVisibleSelectedRulesIds(rules.map((rule) => rule.id)); + }} + setPagination={(paginationQuery) => + setRulesQuery((currentQuery) => ({ ...currentQuery, ...paginationQuery })) + } + /> + + {hasChanges && ( + + )} +
+ ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx new file mode 100644 index 0000000000000..cc537ff19ed64 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useMemo } from 'react'; +import { + Criteria, + EuiLink, + EuiSwitch, + EuiTableFieldDataColumnType, + EuiBasicTable, + EuiBasicTableProps, +} from '@elastic/eui'; +import moment from 'moment'; +import type { RulesState } from './rules_container'; +import * as TEST_SUBJECTS from './test_subjects'; +import * as TEXT from './translations'; +import type { RuleSavedObject } from './use_csp_rules'; + +type RulesTableProps = Pick< + RulesState, + 'loading' | 'error' | 'rules_page' | 'total' | 'perPage' | 'page' +> & { + toggleRule(rule: RuleSavedObject): void; + setSelectedRules(rules: RuleSavedObject[]): void; + setPagination(pagination: Pick): void; + // ForwardRef makes this ref not available in parent callbacks + tableRef: React.RefObject>; +}; + +export const RulesTable = ({ + toggleRule, + setSelectedRules, + setPagination, + perPage: pageSize, + rules_page: items, + page, + tableRef, + total, + loading, + error, +}: RulesTableProps) => { + const columns = useMemo(() => getColumns({ toggleRule }), [toggleRule]); + + const euiPagination: EuiBasicTableProps['pagination'] = { + pageIndex: page, + pageSize, + totalItemCount: total, + pageSizeOptions: [1, 5, 10, 25], + hidePerPageOptions: false, + }; + + const selection: EuiBasicTableProps['selection'] = { + selectable: () => true, + onSelectionChange: setSelectedRules, + }; + + const onTableChange = ({ page: pagination }: Criteria) => { + if (!pagination) return; + setPagination({ page: pagination.index, perPage: pagination.size }); + }; + + return ( + v.id} + /> + ); +}; + +const ruleNameRenderer = (name: string) => ( + + {name} + +); + +const timestampRenderer = (timestamp: string) => + moment.duration(moment().diff(timestamp)).humanize(); + +interface GetColumnProps { + toggleRule: (rule: RuleSavedObject) => void; +} + +const createRuleEnabledSwitchRenderer = + ({ toggleRule }: GetColumnProps) => + (value: boolean, rule: RuleSavedObject) => + ( + toggleRule(rule)} + data-test-subj={TEST_SUBJECTS.getCspRulesTableItemSwitchTestId(rule.attributes.id)} + /> + ); + +const getColumns = ({ + toggleRule, +}: GetColumnProps): Array> => [ + { + field: 'attributes.name', + name: TEXT.RULE_NAME, + width: '60%', + truncateText: true, + render: ruleNameRenderer, + }, + { + field: 'section', // TODO: what field is this? + name: TEXT.SECTION, + width: '15%', + }, + { + field: 'updatedAt', + name: TEXT.UPDATED_AT, + width: '15%', + render: timestampRenderer, + }, + { + field: 'attributes.enabled', + name: TEXT.ENABLED, + render: createRuleEnabledSwitchRenderer({ toggleRule }), + width: '10%', + }, +]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx new file mode 100644 index 0000000000000..011cb5d8f7bc2 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx @@ -0,0 +1,224 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useState } from 'react'; +import { EuiFieldSearch, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; +import useDebounce from 'react-use/lib/useDebounce'; +import * as TEST_SUBJECTS from './test_subjects'; +import * as TEXT from './translations'; +import { RulesBulkActionsMenu } from './rules_bulk_actions_menu'; + +interface RulesTableToolbarProps { + search(value: string): void; + refresh(): void; + bulkEnable(): void; + bulkDisable(): void; + selectAll(): void; + clearSelection(): void; + totalRulesCount: number; + selectedRulesCount: number; + searchValue: string; + isSearching: boolean; +} + +interface CounterProps { + count: number; +} + +interface ButtonProps { + onClick(): void; +} + +export const RulesTableHeader = ({ + search, + refresh, + bulkEnable, + bulkDisable, + selectAll, + clearSelection, + totalRulesCount, + selectedRulesCount, + searchValue, + isSearching, +}: RulesTableToolbarProps) => ( + + + + + + + +); + +const Counters = ({ total, selected }: { total: number; selected: number }) => ( + + + {Spacer} + + +); + +const SelectAllToggle = ({ + isSelectAll, + select, + clear, +}: { + select(): void; + clear(): void; + isSelectAll: boolean; +}) => ( + + {isSelectAll ? : } + +); + +const BulkMenu = ({ + bulkEnable, + bulkDisable, + selectedRulesCount, +}: Pick) => ( + + , + 'data-test-subj': TEST_SUBJECTS.CSP_RULES_TABLE_BULK_ENABLE_BUTTON, + onClick: bulkEnable, + }, + { + icon: 'eyeClosed', + disabled: !selectedRulesCount, + children: , + 'data-test-subj': TEST_SUBJECTS.CSP_RULES_TABLE_BULK_DISABLE_BUTTON, + onClick: bulkDisable, + }, + ]} + /> + +); + +const SEARCH_DEBOUNCE_MS = 300; + +const SearchField = ({ + search, + isSearching, + searchValue, +}: Pick) => { + const [localValue, setLocalValue] = useState(searchValue); + + useDebounce(() => search(localValue), SEARCH_DEBOUNCE_MS, [localValue]); + + return ( + + setLocalValue(e.target.value)} + style={{ minWidth: 150 }} + /> + + ); +}; + +const TotalRulesCount = ({ count }: CounterProps) => ( + }} + /> +); + +const SelectedRulesCount = ({ count }: CounterProps) => ( + }} + /> +); + +const ActivateRulesMenuItemText = ({ count }: CounterProps) => ( + +); + +const DeactivateRulesMenuItemText = ({ count }: CounterProps) => ( + +); + +const RulesCountBold = ({ count }: CounterProps) => ( + <> + {count} + + +); + +const ClearSelectionButton = ({ onClick }: ButtonProps) => ( + + + +); + +const SelectAllButton = ({ onClick }: ButtonProps) => ( + + + +); + +const RefreshButton = ({ onClick }: ButtonProps) => ( + + + {TEXT.REFRESH} + + +); + +const Spacer = ( + +); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/test_subjects.ts new file mode 100644 index 0000000000000..6ba10ef937b66 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/test_subjects.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const CSP_RULES_CONTAINER = 'csp_rules_container'; +export const CSP_RULES_TABLE_ITEM_SWITCH = 'csp_rules_table_item_switch'; +export const CSP_RULES_SAVE_BUTTON = 'csp_rules_table_save_button'; +export const CSP_RULES_TABLE = 'csp_rules_table'; +export const CSP_RULES_TABLE_BULK_MENU_BUTTON = 'csp_rules_table_bulk_menu_button'; +export const CSP_RULES_TABLE_BULK_ENABLE_BUTTON = 'csp_rules_table_bulk_enable_button'; +export const CSP_RULES_TABLE_BULK_DISABLE_BUTTON = 'csp_rules_table_bulk_disable_button'; +export const CSP_RULES_TABLE_REFRESH_BUTTON = 'csp_rules_table_refresh_button'; +export const CSP_RULES_TABLE_SELECT_ALL_BUTTON = 'rules_select_all'; +export const CSP_RULES_TABLE_CLEAR_SELECTION_BUTTON = 'rules_clear_selection'; + +export const getCspRulesTableItemSwitchTestId = (id: string) => + `${CSP_RULES_TABLE_ITEM_SWITCH}_${id}`; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/translations.ts new file mode 100644 index 0000000000000..0f9c279ae4ba5 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/translations.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const SAVE = i18n.translate('xpack.csp.rules.saveButtonLabel', { + defaultMessage: 'Save', +}); + +export const CANCEL = i18n.translate('xpack.csp.rules.cancelButtonLabel', { + defaultMessage: 'Cancel', +}); + +export const UNKNOWN_ERROR = i18n.translate('xpack.csp.rules.unknownErrorMessage', { + defaultMessage: 'Unknown Error', +}); + +export const REFRESH = i18n.translate('xpack.csp.rules.refreshButtonLabel', { + defaultMessage: 'Refresh', +}); + +export const SEARCH = i18n.translate('xpack.csp.rules.searchPlaceholder', { + defaultMessage: 'Search', +}); + +export const BULK_ACTIONS = i18n.translate('xpack.csp.rules.bulkActionsButtonLabel', { + defaultMessage: 'Bulk Actions', +}); + +export const RULE_NAME = i18n.translate('xpack.csp.rules.ruleNameColumnHeaderLabel', { + defaultMessage: 'Rule Name', +}); + +export const SECTION = i18n.translate('xpack.csp.rules.sectionColumnHeaderLabel', { + defaultMessage: 'Section', +}); + +export const UPDATED_AT = i18n.translate('xpack.csp.rules.updatedAtColumnHeaderLabel', { + defaultMessage: 'Updated at', +}); + +export const ENABLED = i18n.translate('xpack.csp.rules.enabledColumnHeaderLabel', { + defaultMessage: 'Enabled', +}); + +export const DISABLE = i18n.translate('xpack.csp.rules.disableLabel', { + defaultMessage: 'Disable', +}); + +export const ENABLE = i18n.translate('xpack.csp.rules.enableLabel', { + defaultMessage: 'Enable', +}); + +export const MISSING_RULES = i18n.translate('xpack.csp.rules.missingRulesMessage', { + defaultMessage: 'Rules are missing', +}); + +export const UPDATE_FAILED = i18n.translate('xpack.csp.rules.updateFailedMessage', { + defaultMessage: 'Update failed', +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts index a11256edaa40e..32e1a2635a857 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts @@ -5,35 +5,31 @@ * 2.0. */ import { useQuery, useMutation, useQueryClient } from 'react-query'; +import { FunctionKeys } from 'utility-types'; import { cspRuleAssetSavedObjectType, type CspRuleSchema } from '../../../common/schemas/csp_rule'; -import type { - SavedObjectsBatchResponse, - SavedObjectsFindOptions, -} from '../../../../../../src/core/public'; +import type { SavedObjectsFindOptions, SimpleSavedObject } from '../../../../../../src/core/public'; import { useKibana } from '../../common/hooks/use_kibana'; +import { UPDATE_FAILED } from './translations'; -export type UseCspRulesOptions = Pick< - SavedObjectsFindOptions, - 'search' | 'searchFields' | 'page' | 'perPage' +export type RuleSavedObject = Omit< + SimpleSavedObject, + FunctionKeys >; -export const useFindCspRules = ({ - search, - searchFields, - page = 1, - perPage = 10, -}: UseCspRulesOptions) => { +export type RulesQuery = Required>; +export type RulesQueryResult = ReturnType; + +export const useFindCspRules = ({ search, page, perPage }: RulesQuery) => { const { savedObjects } = useKibana().services; return useQuery( - [cspRuleAssetSavedObjectType, { search, searchFields, page, perPage }], + [cspRuleAssetSavedObjectType, { search, page, perPage }], () => savedObjects.client.find({ type: cspRuleAssetSavedObjectType, search, - searchFields, - page, - // NOTE: 'name.raw' is a field maping we defined on 'name' so it'd also be sortable - // TODO: this needs to be shared or removed + searchFields: ['name'], + page: 1, + // NOTE: 'name.raw' is a field mapping we defined on 'name' sortField: 'name.raw', perPage, }), @@ -42,21 +38,25 @@ export const useFindCspRules = ({ }; export const useBulkUpdateCspRules = () => { - const { savedObjects } = useKibana().services; + const { savedObjects, notifications } = useKibana().services; const queryClient = useQueryClient(); return useMutation( (rules: CspRuleSchema[]) => - savedObjects.client.bulkUpdate( + savedObjects.client.bulkUpdate( rules.map((rule) => ({ type: cspRuleAssetSavedObjectType, id: rule.id, attributes: rule, })) - // TODO: fix bulkUpdate types in core - ) as Promise>, + ), { + onError: (err) => { + if (err instanceof Error) notifications.toasts.addError(err, { title: UPDATE_FAILED }); + else notifications.toasts.addDanger(UPDATE_FAILED); + }, onSettled: () => + // Invalidate all queries for simplicity queryClient.invalidateQueries({ queryKey: cspRuleAssetSavedObjectType, exact: false, From 1568eed67ba592f31717183368731bc3c4f838d1 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 9 Mar 2022 08:35:00 -0500 Subject: [PATCH 110/140] [Fleet] Fix empty assets on package install (#127070) --- .../sections/epm/screens/detail/index.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 2ba625ea420e3..d002a743e77bc 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -138,9 +138,20 @@ export function Detail() { data: packageInfoData, error: packageInfoError, isLoading: packageInfoLoading, + isInitialRequest: packageIsInitialRequest, + resendRequest: refreshPackageInfo, } = useGetPackageInfoByKey(pkgName, pkgVersion); - const isLoading = packageInfoLoading || permissionCheck.isLoading; + // Refresh package info when status change + const [oldPackageInstallStatus, setOldPackageStatus] = useState(packageInstallStatus); + useEffect(() => { + if (oldPackageInstallStatus === 'not_installed' && packageInstallStatus === 'installed') { + setOldPackageStatus(oldPackageInstallStatus); + refreshPackageInfo(); + } + }, [packageInstallStatus, oldPackageInstallStatus, refreshPackageInfo]); + + const isLoading = (packageInfoLoading && !packageIsInitialRequest) || permissionCheck.isLoading; const showCustomTab = useUIExtension(packageInfoData?.item.name ?? '', 'package-detail-custom') !== undefined; From 12f9e3c11c957e5ad6dc4499c58c0ad5255b00ee Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Wed, 9 Mar 2022 14:46:03 +0100 Subject: [PATCH 111/140] [APM] Load list of services from ML/terms enum (#126581) --- .../plugins/apm/common/service_inventory.ts | 20 +++ .../app/service_inventory/index.tsx | 73 +++++--- .../service_inventory.stories.tsx | 4 + .../service_inventory/service_list/index.tsx | 21 +-- .../get_services/get_health_statuses.ts | 10 +- .../get_sorted_and_filtered_services.ts | 80 +++++++++ .../apm/server/routes/services/route.ts | 42 +++++ .../common/utils/create_and_run_apm_ml_job.ts | 38 +++++ .../tests/anomalies/anomaly_charts.spec.ts | 67 +------- .../sorted_and_filtered_services.spec.ts | 157 ++++++++++++++++++ .../failed_transaction_correlations.ts | 5 +- .../apm/correlations/latency_correlations.ts | 5 +- 12 files changed, 416 insertions(+), 106 deletions(-) create mode 100644 x-pack/plugins/apm/common/service_inventory.ts create mode 100644 x-pack/plugins/apm/server/routes/services/get_services/get_sorted_and_filtered_services.ts create mode 100644 x-pack/test/apm_api_integration/common/utils/create_and_run_apm_ml_job.ts create mode 100644 x-pack/test/apm_api_integration/tests/services/sorted_and_filtered_services.spec.ts diff --git a/x-pack/plugins/apm/common/service_inventory.ts b/x-pack/plugins/apm/common/service_inventory.ts new file mode 100644 index 0000000000000..b7c8c0ea90a58 --- /dev/null +++ b/x-pack/plugins/apm/common/service_inventory.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AgentName } from '../typings/es_schemas/ui/fields/agent'; +import { ServiceHealthStatus } from './service_health_status'; + +export interface ServiceListItem { + serviceName: string; + healthStatus?: ServiceHealthStatus; + transactionType?: string; + agentName?: AgentName; + throughput?: number; + latency?: number | null; + transactionErrorRate?: number | null; + environments?: string[]; +} diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index 1e736409a9604..cc4cbd975f5cd 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -12,21 +12,20 @@ import uuid from 'uuid'; import { useAnomalyDetectionJobsContext } from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context'; import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params'; import { useLocalStorage } from '../../../hooks/use_local_storage'; -import { useAnyOfApmParams } from '../../../hooks/use_apm_params'; +import { useApmParams } from '../../../hooks/use_apm_params'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { useTimeRange } from '../../../hooks/use_time_range'; import { SearchBar } from '../../shared/search_bar'; import { getTimeRangeComparison } from '../../shared/time_comparison/get_time_range_comparison'; import { ServiceList } from './service_list'; import { MLCallout, shouldDisplayMlCallout } from '../../shared/ml_callout'; +import { joinByKey } from '../../../../common/utils/join_by_key'; const initialData = { requestId: '', - mainStatisticsData: { - items: [], - hasHistoricalData: true, - hasLegacyData: false, - }, + items: [], + hasHistoricalData: true, + hasLegacyData: false, }; function useServicesFetcher() { @@ -36,7 +35,7 @@ function useServicesFetcher() { const { query: { rangeFrom, rangeTo, environment, kuery }, - } = useAnyOfApmParams('/services/{serviceName}', '/services'); + } = useApmParams('/services'); const { start, end } = useTimeRange({ rangeFrom, rangeTo }); @@ -47,7 +46,23 @@ function useServicesFetcher() { comparisonType, }); - const { data = initialData, status: mainStatisticsStatus } = useFetcher( + const sortedAndFilteredServicesFetch = useFetcher( + (callApmApi) => { + return callApmApi('GET /internal/apm/sorted_and_filtered_services', { + params: { + query: { + start, + end, + environment, + kuery, + }, + }, + }); + }, + [start, end, environment, kuery] + ); + + const mainStatisticsFetch = useFetcher( (callApmApi) => { if (start && end) { return callApmApi('GET /internal/apm/services', { @@ -62,7 +77,7 @@ function useServicesFetcher() { }).then((mainStatisticsData) => { return { requestId: uuid(), - mainStatisticsData, + ...mainStatisticsData, }; }); } @@ -70,9 +85,9 @@ function useServicesFetcher() { [environment, kuery, start, end] ); - const { mainStatisticsData, requestId } = data; + const { data: mainStatisticsData = initialData } = mainStatisticsFetch; - const { data: comparisonData } = useFetcher( + const comparisonFetch = useFetcher( (callApmApi) => { if (start && end && mainStatisticsData.items.length) { return callApmApi('GET /internal/apm/services/detailed_statistics', { @@ -96,20 +111,23 @@ function useServicesFetcher() { }, // only fetches detailed statistics when requestId is invalidated by main statistics api call or offset is changed // eslint-disable-next-line react-hooks/exhaustive-deps - [requestId, offset], + [mainStatisticsData.requestId, offset], { preservePreviousData: false } ); return { - mainStatisticsData, - mainStatisticsStatus, - comparisonData, + sortedAndFilteredServicesFetch, + mainStatisticsFetch, + comparisonFetch, }; } export function ServiceInventory() { - const { mainStatisticsData, mainStatisticsStatus, comparisonData } = - useServicesFetcher(); + const { + sortedAndFilteredServicesFetch, + mainStatisticsFetch, + comparisonFetch, + } = useServicesFetcher(); const { anomalyDetectionSetupState } = useAnomalyDetectionJobsContext(); @@ -122,8 +140,13 @@ export function ServiceInventory() { !userHasDismissedCallout && shouldDisplayMlCallout(anomalyDetectionSetupState); - const isLoading = mainStatisticsStatus === FETCH_STATUS.LOADING; - const isFailure = mainStatisticsStatus === FETCH_STATUS.FAILURE; + const isLoading = + sortedAndFilteredServicesFetch.status === FETCH_STATUS.LOADING || + (sortedAndFilteredServicesFetch.status === FETCH_STATUS.SUCCESS && + sortedAndFilteredServicesFetch.data?.services.length === 0 && + mainStatisticsFetch.status === FETCH_STATUS.LOADING); + + const isFailure = mainStatisticsFetch.status === FETCH_STATUS.FAILURE; const noItemsMessage = ( ); + const items = joinByKey( + [ + ...(sortedAndFilteredServicesFetch.data?.services ?? []), + ...(mainStatisticsFetch.data?.items ?? []), + ], + 'serviceName' + ); + return ( <> @@ -154,8 +185,8 @@ export function ServiceInventory() {
diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.stories.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.stories.tsx index bececfb545ba9..01430c93b4b5a 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.stories.tsx @@ -30,6 +30,10 @@ const stories: Meta<{}> = { switch (endpoint) { case '/internal/apm/services': return { items: [] }; + + case '/internal/apm/sorted_and_filtered_services': + return { services: [] }; + default: return {}; } diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx index 760b849775429..2d01a11d92186 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx @@ -17,7 +17,6 @@ import { i18n } from '@kbn/i18n'; import { TypeOf } from '@kbn/typed-react-router-config'; import { orderBy } from 'lodash'; import React, { useMemo } from 'react'; -import { ValuesType } from 'utility-types'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { @@ -46,14 +45,11 @@ import { getTimeSeriesColor, } from '../../../shared/charts/helper/get_timeseries_color'; import { HealthBadge } from './health_badge'; +import { ServiceListItem } from '../../../../../common/service_inventory'; -type ServiceListAPIResponse = APIReturnType<'GET /internal/apm/services'>; -type Items = ServiceListAPIResponse['items']; type ServicesDetailedStatisticsAPIResponse = APIReturnType<'GET /internal/apm/services/detailed_statistics'>; -type ServiceListItem = ValuesType; - function formatString(value?: string | null) { return value || NOT_AVAILABLE_LABEL; } @@ -239,7 +235,7 @@ export function getServiceColumns({ } interface Props { - items: Items; + items: ServiceListItem[]; comparisonData?: ServicesDetailedStatisticsAPIResponse; noItemsMessage?: React.ReactNode; isLoading: boolean; @@ -287,9 +283,8 @@ export function ServiceList({ ] ); - const initialSortField = displayHealthStatus - ? 'healthStatus' - : 'transactionsPerMinute'; + const initialSortField = displayHealthStatus ? 'healthStatus' : 'serviceName'; + const initialSortDirection = displayHealthStatus ? 'desc' : 'asc'; return ( @@ -336,9 +331,9 @@ export function ServiceList({ items={items} noItemsMessage={noItemsMessage} initialSortField={initialSortField} - initialSortDirection="desc" + initialSortDirection={initialSortDirection} sortFn={(itemsToSort, sortField, sortDirection) => { - // For healthStatus, sort items by healthStatus first, then by TPM + // For healthStatus, sort items by healthStatus first, then by name return sortField === 'healthStatus' ? orderBy( itemsToSort, @@ -348,9 +343,9 @@ export function ServiceList({ ? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus) : -1; }, - (item) => item.throughput ?? 0, + (item) => item.serviceName.toLowerCase(), ], - [sortDirection, sortDirection] + [sortDirection, sortDirection === 'asc' ? 'desc' : 'asc'] ) : orderBy( itemsToSort, diff --git a/x-pack/plugins/apm/server/routes/services/get_services/get_health_statuses.ts b/x-pack/plugins/apm/server/routes/services/get_services/get_health_statuses.ts index 65fb04b821ffa..4e8795aacc228 100644 --- a/x-pack/plugins/apm/server/routes/services/get_services/get_health_statuses.ts +++ b/x-pack/plugins/apm/server/routes/services/get_services/get_health_statuses.ts @@ -6,14 +6,16 @@ */ import { getSeverity } from '../../../../common/anomaly_detection'; -import { getServiceHealthStatus } from '../../../../common/service_health_status'; +import { + getServiceHealthStatus, + ServiceHealthStatus, +} from '../../../../common/service_health_status'; import { getServiceAnomalies } from '../../../routes/service_map/get_service_anomalies'; import { ServicesItemsSetup } from './get_services_items'; interface AggregationParams { environment: string; setup: ServicesItemsSetup; - searchAggregatedTransactions: boolean; start: number; end: number; } @@ -23,7 +25,9 @@ export const getHealthStatuses = async ({ setup, start, end, -}: AggregationParams) => { +}: AggregationParams): Promise< + Array<{ serviceName: string; healthStatus: ServiceHealthStatus }> +> => { if (!setup.ml) { return []; } diff --git a/x-pack/plugins/apm/server/routes/services/get_services/get_sorted_and_filtered_services.ts b/x-pack/plugins/apm/server/routes/services/get_services/get_sorted_and_filtered_services.ts new file mode 100644 index 0000000000000..5a7df14fa3186 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/services/get_services/get_sorted_and_filtered_services.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/logging'; +import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames'; +import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; +import { Environment } from '../../../../common/environment_rt'; +import { ProcessorEvent } from '../../../../common/processor_event'; +import { joinByKey } from '../../../../common/utils/join_by_key'; +import { Setup } from '../../../lib/helpers/setup_request'; +import { getHealthStatuses } from './get_health_statuses'; + +export async function getSortedAndFilteredServices({ + setup, + start, + end, + environment, + logger, +}: { + setup: Setup; + start: number; + end: number; + environment: Environment; + logger: Logger; +}) { + const { apmEventClient } = setup; + + async function getServicesFromTermsEnum() { + if (environment !== ENVIRONMENT_ALL.value) { + return []; + } + const response = await apmEventClient.termsEnum( + 'get_services_from_terms_enum', + { + apm: { + events: [ + ProcessorEvent.transaction, + ProcessorEvent.span, + ProcessorEvent.metric, + ProcessorEvent.error, + ], + }, + body: { + size: 500, + field: SERVICE_NAME, + }, + } + ); + + return response.terms; + } + + const [servicesWithHealthStatuses, serviceNamesFromTermsEnum] = + await Promise.all([ + getHealthStatuses({ + setup, + start, + end, + environment, + }).catch((error) => { + logger.error(error); + return []; + }), + getServicesFromTermsEnum(), + ]); + + const services = joinByKey( + [ + ...servicesWithHealthStatuses, + ...serviceNamesFromTermsEnum.map((serviceName) => ({ serviceName })), + ], + 'serviceName' + ); + + return services; +} diff --git a/x-pack/plugins/apm/server/routes/services/route.ts b/x-pack/plugins/apm/server/routes/services/route.ts index 55f7b4f14b7b6..7c69f3b1df802 100644 --- a/x-pack/plugins/apm/server/routes/services/route.ts +++ b/x-pack/plugins/apm/server/routes/services/route.ts @@ -52,6 +52,8 @@ import { ML_ERRORS } from '../../../common/anomaly_detection'; import { ScopedAnnotationsClient } from '../../../../observability/server'; import { Annotation } from './../../../../observability/common/annotations'; import { ConnectionStatsItemWithImpact } from './../../../common/connections'; +import { getSortedAndFilteredServices } from './get_services/get_sorted_and_filtered_services'; +import { ServiceHealthStatus } from './../../../common/service_health_status'; const servicesRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/services', @@ -1224,6 +1226,45 @@ const serviceAnomalyChartsRoute = createApmServerRoute({ }, }); +const sortedAndFilteredServicesRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/sorted_and_filtered_services', + options: { + tags: ['access:apm'], + }, + params: t.type({ + query: t.intersection([rangeRt, environmentRt, kueryRt]), + }), + handler: async ( + resources + ): Promise<{ + services: Array<{ + serviceName: string; + healthStatus?: ServiceHealthStatus; + }>; + }> => { + const { + query: { start, end, environment, kuery }, + } = resources.params; + + if (kuery) { + return { + services: [], + }; + } + + const setup = await setupRequest(resources); + return { + services: await getSortedAndFilteredServices({ + setup, + start, + end, + environment, + logger: resources.logger, + }), + }; + }, +}); + export const serviceRouteRepository = { ...servicesRoute, ...servicesDetailedStatisticsRoute, @@ -1245,4 +1286,5 @@ export const serviceRouteRepository = { ...serviceAlertsRoute, ...serviceInfrastructureRoute, ...serviceAnomalyChartsRoute, + ...sortedAndFilteredServicesRoute, }; diff --git a/x-pack/test/apm_api_integration/common/utils/create_and_run_apm_ml_job.ts b/x-pack/test/apm_api_integration/common/utils/create_and_run_apm_ml_job.ts new file mode 100644 index 0000000000000..bd03493039b49 --- /dev/null +++ b/x-pack/test/apm_api_integration/common/utils/create_and_run_apm_ml_job.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import job from '../../../../plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/apm_tx_metrics.json'; +import datafeed from '../../../../plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/datafeed_apm_tx_metrics.json'; +import { MlApi } from '../../../functional/services/ml/api'; + +export function createAndRunApmMlJob({ ml, environment }: { ml: MlApi; environment: string }) { + return ml.createAndRunAnomalyDetectionLookbackJob( + // @ts-expect-error not entire job config + { + ...job, + job_id: `apm-tx-metrics-${environment}`, + allow_lazy_open: false, + custom_settings: { + job_tags: { + apm_ml_version: '3', + environment, + }, + }, + }, + { + ...datafeed, + job_id: `apm-tx-metrics-${environment}`, + indices: ['apm-*'], + datafeed_id: `apm-tx-metrics-${environment}-datafeed`, + query: { + bool: { + filter: [...datafeed.query.bool.filter, { term: { 'service.environment': environment } }], + }, + }, + } + ); +} diff --git a/x-pack/test/apm_api_integration/tests/anomalies/anomaly_charts.spec.ts b/x-pack/test/apm_api_integration/tests/anomalies/anomaly_charts.spec.ts index 00bdc258fb40f..e06c519a978ab 100644 --- a/x-pack/test/apm_api_integration/tests/anomalies/anomaly_charts.spec.ts +++ b/x-pack/test/apm_api_integration/tests/anomalies/anomaly_charts.spec.ts @@ -10,10 +10,9 @@ import { range, omit } from 'lodash'; import { apm, timerange } from '@elastic/apm-synthtrace'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { ApmApiError } from '../../common/apm_api_supertest'; -import job from '../../../../plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/apm_tx_metrics.json'; -import datafeed from '../../../../plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/datafeed_apm_tx_metrics.json'; import { ServiceAnomalyTimeseries } from '../../../../plugins/apm/common/anomaly_detection/service_anomaly_timeseries'; import { ApmMlDetectorType } from '../../../../plugins/apm/common/anomaly_detection/apm_ml_detectors'; +import { createAndRunApmMlJob } from '../../common/utils/create_and_run_apm_ml_job'; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); @@ -169,62 +168,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('with ml jobs', () => { before(async () => { await Promise.all([ - ml.createAndRunAnomalyDetectionLookbackJob( - // @ts-expect-error not entire job config - { - ...job, - job_id: 'apm-tx-metrics-prod', - allow_lazy_open: false, - custom_settings: { - job_tags: { - apm_ml_version: '3', - environment: 'production', - }, - }, - }, - { - ...datafeed, - job_id: 'apm-tx-metrics-prod', - indices: ['apm-*'], - datafeed_id: 'apm-tx-metrics-prod-datafeed', - query: { - bool: { - filter: [ - ...datafeed.query.bool.filter, - { term: { 'service.environment': 'production' } }, - ], - }, - }, - } - ), - ml.createAndRunAnomalyDetectionLookbackJob( - // @ts-expect-error not entire job config - { - ...job, - job_id: 'apm-tx-metrics-development', - allow_lazy_open: false, - custom_settings: { - job_tags: { - apm_ml_version: '3', - environment: 'development', - }, - }, - }, - { - ...datafeed, - job_id: 'apm-tx-metrics-development', - indices: ['apm-*'], - datafeed_id: 'apm-tx-metrics-development-datafeed', - query: { - bool: { - filter: [ - ...datafeed.query.bool.filter, - { term: { 'service.environment': 'development' } }, - ], - }, - }, - } - ), + createAndRunApmMlJob({ environment: 'production', ml }), + createAndRunApmMlJob({ environment: 'development', ml }), ]); }); @@ -288,7 +233,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(omitTimeseriesData(latencySeries)).to.eql({ type: ApmMlDetectorType.txLatency, - jobId: 'apm-tx-metrics-prod', + jobId: 'apm-tx-metrics-production', serviceName: 'a', environment: 'production', transactionType: 'request', @@ -297,7 +242,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(omitTimeseriesData(throughputSeries)).to.eql({ type: ApmMlDetectorType.txThroughput, - jobId: 'apm-tx-metrics-prod', + jobId: 'apm-tx-metrics-production', serviceName: 'a', environment: 'production', transactionType: 'request', @@ -306,7 +251,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(omitTimeseriesData(failureRateSeries)).to.eql({ type: ApmMlDetectorType.txFailureRate, - jobId: 'apm-tx-metrics-prod', + jobId: 'apm-tx-metrics-production', serviceName: 'a', environment: 'production', transactionType: 'request', diff --git a/x-pack/test/apm_api_integration/tests/services/sorted_and_filtered_services.spec.ts b/x-pack/test/apm_api_integration/tests/services/sorted_and_filtered_services.spec.ts new file mode 100644 index 0000000000000..b529ab36c5637 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/services/sorted_and_filtered_services.spec.ts @@ -0,0 +1,157 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ValuesType } from 'utility-types'; +import { apm, timerange } from '@elastic/apm-synthtrace'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { createAndRunApmMlJob } from '../../common/utils/create_and_run_apm_ml_job'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); + const synthtraceClient = getService('synthtraceEsClient'); + const apmApiClient = getService('apmApiClient'); + const ml = getService('ml'); + + const start = '2021-01-01T12:00:00.000Z'; + const end = '2021-08-01T12:00:00.000Z'; + + // the terms enum API will return names for deleted services, + // so we add a prefix to make sure we don't get data from other + // tests + const SERVICE_NAME_PREFIX = 'sorted_and_filtered_'; + + async function getSortedAndFilteredServices({ + environment = 'ENVIRONMENT_ALL', + kuery = '', + }: { environment?: string; kuery?: string } = {}) { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/sorted_and_filtered_services', + params: { + query: { + start, + end, + environment, + kuery, + }, + }, + }); + + return response.body.services + .filter((service) => service.serviceName.startsWith(SERVICE_NAME_PREFIX)) + .map((service) => ({ + ...service, + serviceName: service.serviceName.replace(SERVICE_NAME_PREFIX, ''), + })); + } + + type ServiceListItem = ValuesType>>; + + registry.when( + 'Sorted and filtered services', + { config: 'trial', archives: ['apm_mappings_only_8.0.0'] }, + () => { + before(async () => { + const serviceA = apm.service(SERVICE_NAME_PREFIX + 'a', 'production', 'java').instance('a'); + + const serviceB = apm.service(SERVICE_NAME_PREFIX + 'b', 'development', 'go').instance('b'); + + const serviceC = apm.service(SERVICE_NAME_PREFIX + 'c', 'development', 'go').instance('c'); + + const spikeStart = new Date('2021-01-07T12:00:00.000Z').getTime(); + const spikeEnd = new Date('2021-01-07T14:00:00.000Z').getTime(); + + const eventsWithinTimerange = timerange(new Date(start).getTime(), new Date(end).getTime()) + .interval('15m') + .rate(1) + .spans((timestamp) => { + const isInSpike = spikeStart <= timestamp && spikeEnd >= timestamp; + return [ + ...serviceA + .transaction('GET /api') + .duration(isInSpike ? 1000 : 1100) + .timestamp(timestamp) + .serialize(), + ...serviceB + .transaction('GET /api') + .duration(isInSpike ? 1000 : 4000) + .timestamp(timestamp) + .serialize(), + ]; + }); + + const eventsOutsideOfTimerange = timerange( + new Date('2021-01-01T00:00:00.000Z').getTime(), + new Date(start).getTime() - 1 + ) + .interval('15m') + .rate(1) + .spans((timestamp) => { + return serviceC + .transaction('GET /api', 'custom') + .duration(1000) + .timestamp(timestamp) + .serialize(); + }); + + await synthtraceClient.index(eventsWithinTimerange.concat(eventsOutsideOfTimerange)); + + await Promise.all([ + createAndRunApmMlJob({ environment: 'production', ml }), + createAndRunApmMlJob({ environment: 'development', ml }), + ]); + }); + + after(() => { + return Promise.all([synthtraceClient.clean(), ml.cleanMlIndices()]); + }); + + describe('with no kuery or environment are set', () => { + let items: ServiceListItem[]; + + before(async () => { + items = await getSortedAndFilteredServices(); + }); + + it('returns services based on the terms enum API and ML data', () => { + const serviceNames = items.map((item) => item.serviceName); + + expect(serviceNames.sort()).to.eql(['a', 'b', 'c']); + }); + }); + + describe('with kuery set', () => { + let items: ServiceListItem[]; + + before(async () => { + items = await getSortedAndFilteredServices({ + kuery: 'service.name:*', + }); + }); + + it('does not return any services', () => { + expect(items.length).to.be(0); + }); + }); + + describe('with environment set to production', () => { + let items: ServiceListItem[]; + + before(async () => { + items = await getSortedAndFilteredServices({ + environment: 'production', + }); + }); + + it('returns services for production only', () => { + const serviceNames = items.map((item) => item.serviceName); + + expect(serviceNames.sort()).to.eql(['a']); + }); + }); + } + ); +} diff --git a/x-pack/test/functional/apps/apm/correlations/failed_transaction_correlations.ts b/x-pack/test/functional/apps/apm/correlations/failed_transaction_correlations.ts index d9a5ea35e5393..f70bd4736bd7e 100644 --- a/x-pack/test/functional/apps/apm/correlations/failed_transaction_correlations.ts +++ b/x-pack/test/functional/apps/apm/correlations/failed_transaction_correlations.ts @@ -36,6 +36,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/infra/8.0.0/metrics_and_apm'); await spacesService.delete('custom_space'); }); @@ -54,10 +55,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.existOrFail('apmMainContainer', { timeout: 10000, }); - - const apmMainContainerText = await testSubjects.getVisibleTextAll('apmMainContainer'); - const apmMainContainerTextItems = apmMainContainerText[0].split('\n'); - expect(apmMainContainerTextItems).to.contain('No services found'); }); }); diff --git a/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts b/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts index af3633798133b..200b3367b9723 100644 --- a/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts +++ b/x-pack/test/functional/apps/apm/correlations/latency_correlations.ts @@ -37,6 +37,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/infra/8.0.0/metrics_and_apm'); await spacesService.delete('custom_space'); }); @@ -55,10 +56,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.existOrFail('apmMainContainer', { timeout: 10000, }); - - const apmMainContainerText = await testSubjects.getVisibleTextAll('apmMainContainer'); - const apmMainContainerTextItems = apmMainContainerText[0].split('\n'); - expect(apmMainContainerTextItems).to.contain('No services found'); }); }); From e930db1765eaee048fe13162d6d50a5fa84e0bc6 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Wed, 9 Mar 2022 14:51:26 +0100 Subject: [PATCH 112/140] [Cases] Fix persistent title error in EditableTitle (#127254) --- .../header_page/editable_title.test.tsx | 43 ++++++++++++++ .../components/header_page/editable_title.tsx | 58 ++++++++++--------- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx b/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx index 9cf956c78fe72..111cc4940ac59 100644 --- a/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx +++ b/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx @@ -221,6 +221,49 @@ describe('EditableTitle', () => { ); }); + it('does not show an error after a previous edit error was displayed', () => { + const longTitle = + 'This is a title that should not be saved as it is longer than 64 characters.'; + + const shortTitle = 'My title'; + const wrapper = mount( + + + + ); + + wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click'); + wrapper.update(); + + // simualte a long title + wrapper + .find('input[data-test-subj="editable-title-input-field"]') + .simulate('change', { target: { value: longTitle } }); + + wrapper.find('button[data-test-subj="editable-title-submit-btn"]').simulate('click'); + wrapper.update(); + expect(wrapper.find('.euiFormErrorText').text()).toBe( + 'The length of the title is too long. The maximum length is 64.' + ); + + // write a shorter one + wrapper + .find('input[data-test-subj="editable-title-input-field"]') + .simulate('change', { target: { value: shortTitle } }); + wrapper.update(); + + // submit the form + wrapper.find('button[data-test-subj="editable-title-submit-btn"]').simulate('click'); + wrapper.update(); + + // edit again + wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click'); + wrapper.update(); + + // no error should appear + expect(wrapper.find('.euiFormErrorText').length).toBe(0); + }); + describe('Badges', () => { let appMock: AppMockRenderer; diff --git a/x-pack/plugins/cases/public/components/header_page/editable_title.tsx b/x-pack/plugins/cases/public/components/header_page/editable_title.tsx index 95e3f6f4a4bcb..0b142ca40a548 100644 --- a/x-pack/plugins/cases/public/components/header_page/editable_title.tsx +++ b/x-pack/plugins/cases/public/components/header_page/editable_title.tsx @@ -71,6 +71,7 @@ const EditableTitleComponent: React.FC = ({ onSubmit(newTitle); } setEditMode(false); + setErrors([]); }, [newTitle, onSubmit, title]); const handleOnChange = useCallback( @@ -82,39 +83,42 @@ const EditableTitleComponent: React.FC = ({ return editMode ? ( - - + + - - - - {i18n.SAVE} - - - - - {i18n.CANCEL} - - - - + + + {i18n.SAVE} + + + + + {i18n.CANCEL} + + ) : ( From 2e6d6c895452878a084cf788ad66ea99f695e03c Mon Sep 17 00:00:00 2001 From: Baturalp Gurdin <9674241+suchcodemuchwow@users.noreply.github.com> Date: Wed, 9 Mar 2022 15:09:57 +0100 Subject: [PATCH 113/140] add new cluster id (#127265) * add new cluster id to single user performance tests * refactor: remove duplicate login journey --- .../scripts/steps/functional/performance_sub_playwright.sh | 2 +- x-pack/test/performance/config.playwright.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.buildkite/scripts/steps/functional/performance_sub_playwright.sh b/.buildkite/scripts/steps/functional/performance_sub_playwright.sh index 771afe6c78c60..596304d156cf0 100644 --- a/.buildkite/scripts/steps/functional/performance_sub_playwright.sh +++ b/.buildkite/scripts/steps/functional/performance_sub_playwright.sh @@ -23,7 +23,7 @@ cd "$XPACK_DIR" jobId=$(npx uuid) export TEST_JOB_ID="$jobId" -journeys=("login" "ecommerce_dashboard" "flight_dashboard" "web_logs_dashboard" "promotion_tracking_dashboard") +journeys=("ecommerce_dashboard" "flight_dashboard" "web_logs_dashboard" "promotion_tracking_dashboard") for i in "${journeys[@]}"; do echo "JOURNEY[${i}] is running" diff --git a/x-pack/test/performance/config.playwright.ts b/x-pack/test/performance/config.playwright.ts index 1866d97b438bb..72f809b07017c 100644 --- a/x-pack/test/performance/config.playwright.ts +++ b/x-pack/test/performance/config.playwright.ts @@ -11,8 +11,8 @@ import { services } from './services'; import { pageObjects } from './page_objects'; // These "secret" values are intentionally written in the source. We would make the APM server accept anonymous traffic if we could -const APM_SERVER_URL = 'https://2fad4006bf784bb8a54e52f4a5862609.apm.us-west1.gcp.cloud.es.io:443'; -const APM_PUBLIC_TOKEN = 'Q5q5rWQEw6tKeirBpw'; +const APM_SERVER_URL = 'https://kibana-ops-e2e-perf.apm.us-central1.gcp.cloud.es.io:443'; +const APM_PUBLIC_TOKEN = 'CTs9y3cvcfq13bQqsB'; export default async function ({ readConfigFile, log }: FtrConfigProviderContext) { const functionalConfig = await readConfigFile(require.resolve('../functional/config')); From b2d649f7c5018c0333e7d7647f6c1a11cfa381ce Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Wed, 9 Mar 2022 07:19:14 -0700 Subject: [PATCH 114/140] Updates advanced settings `kibana.json` team ownership (#127186) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/advanced_settings/kibana.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/advanced_settings/kibana.json b/src/plugins/advanced_settings/kibana.json index 033d5e9da9eab..98b7ec6d7e6d8 100644 --- a/src/plugins/advanced_settings/kibana.json +++ b/src/plugins/advanced_settings/kibana.json @@ -7,7 +7,7 @@ "optionalPlugins": ["home", "usageCollection"], "requiredBundles": ["kibanaReact", "kibanaUtils", "home"], "owner": { - "name": "Vis Editors", - "githubTeam": "kibana-vis-editors" + "name": "Kibana Core", + "githubTeam": "kibana-core" } } From b1d6f87abcf5bbdcdccb03e67cfde833965e77a3 Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Wed, 9 Mar 2022 14:21:12 +0000 Subject: [PATCH 115/140] [APM] Invalidate keys created by other when user has privileges (#127002) * [APM] Invalidate keys created by other when user has privilages * pr review --- .../routes/agent_keys/invalidate_agent_key.ts | 7 +++---- .../apm/server/routes/agent_keys/route.ts | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/apm/server/routes/agent_keys/invalidate_agent_key.ts b/x-pack/plugins/apm/server/routes/agent_keys/invalidate_agent_key.ts index 99c5008718403..ec244f8e9bee3 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/invalidate_agent_key.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/invalidate_agent_key.ts @@ -9,17 +9,16 @@ import { ApmPluginRequestHandlerContext } from '../typings'; export async function invalidateAgentKey({ context, id, + isAdmin, }: { context: ApmPluginRequestHandlerContext; id: string; + isAdmin: boolean; }) { const { invalidated_api_keys: invalidatedAgentKeys } = await context.core.elasticsearch.client.asCurrentUser.security.invalidateApiKey( { - body: { - ids: [id], - owner: true, - }, + body: { ids: [id], owner: !isAdmin }, } ); diff --git a/x-pack/plugins/apm/server/routes/agent_keys/route.ts b/x-pack/plugins/apm/server/routes/agent_keys/route.ts index 90ffbbf8a656e..9b01153f73910 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/route.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/route.ts @@ -70,15 +70,29 @@ const invalidateAgentKeyRoute = createApmServerRoute({ body: t.type({ id: t.string }), }), handler: async (resources): Promise<{ invalidatedAgentKeys: string[] }> => { - const { context, params } = resources; - + const { + context, + params, + plugins: { security }, + } = resources; const { body: { id }, } = params; + if (!security) { + throw Boom.internal(SECURITY_REQUIRED_MESSAGE); + } + + const securityPluginStart = await security.start(); + const { isAdmin } = await getAgentKeysPrivileges({ + context, + securityPluginStart, + }); + const invalidatedKeys = await invalidateAgentKey({ context, id, + isAdmin, }); return invalidatedKeys; From 46a739e7d8861ac302f53112cb4e21f132ce2b86 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 9 Mar 2022 16:26:59 +0200 Subject: [PATCH 116/140] [Lens] Fix type check failure (#127277) --- x-pack/plugins/lens/public/xy_visualization/visualization.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 17f397980b621..69349b1de3445 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -307,7 +307,7 @@ export const getXyVisualization = ({ ...(palette && { palette }), yConfig, layerType: isReferenceLine ? layerTypes.REFERENCELINE : layerTypes.DATA, - }; + } as XYLayerConfig; const newLayers = prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)); From e932d8394485b2f490bce5e5f154afc74de62662 Mon Sep 17 00:00:00 2001 From: Lee Drengenberg Date: Wed, 9 Mar 2022 08:30:19 -0600 Subject: [PATCH 117/140] [Archive Migration] dashboard/current/kibana (#126293) * switch from es_archive to kbn_archive * another test conversion * add the kbn_archive * remove unused esArchiver * kbn_archive to full replace es_archiver/dashboard/current/kibana * finish this test * to fix this test we have to unload 2 index patterns * had to re-make the kbn_archive from 8.0 instead of 7.17 * cleanup saved objects in before method * remove unused esArchiver * remove unused dashboard/current/kibana es_archiver files * refactor clean to cleanStandardList * a few more tests using the es_archive * cleanup and uncomment smoketest * Apply suggestions from code review Co-authored-by: Spencer * update for code review Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Spencer --- .../kbn_client/kbn_client_saved_objects.ts | 19 + test/examples/embeddables/dashboard.ts | 6 +- .../apps/dashboard/copy_panel_to.ts | 7 +- .../dashboard/create_and_add_embeddables.ts | 10 +- .../apps/dashboard/dashboard_back_button.ts | 7 +- .../dashboard_controls_integration.ts | 11 +- .../dashboard/dashboard_error_handling.ts | 16 +- .../apps/dashboard/dashboard_filter_bar.ts | 13 +- .../apps/dashboard/dashboard_filtering.ts | 13 +- .../apps/dashboard/dashboard_grid.ts | 10 +- .../apps/dashboard/dashboard_options.ts | 10 +- .../apps/dashboard/dashboard_query_bar.ts | 9 +- .../apps/dashboard/dashboard_saved_query.ts | 10 +- .../apps/dashboard/dashboard_snapshots.ts | 7 +- .../dashboard/dashboard_unsaved_listing.ts | 10 +- .../apps/dashboard/dashboard_unsaved_state.ts | 10 +- .../apps/dashboard/data_shared_attributes.ts | 7 +- .../dashboard/edit_embeddable_redirects.ts | 10 +- .../apps/dashboard/edit_visualizations.js | 10 +- test/functional/apps/dashboard/embed_mode.ts | 7 +- .../apps/dashboard/embeddable_data_grid.ts | 9 +- .../apps/dashboard/embeddable_library.ts | 6 +- .../apps/dashboard/embeddable_rendering.ts | 8 +- .../apps/dashboard/empty_dashboard.ts | 7 +- .../apps/dashboard/full_screen_mode.ts | 10 +- test/functional/apps/dashboard/legacy_urls.ts | 7 +- .../apps/dashboard/panel_expand_toggle.ts | 10 +- .../apps/dashboard/saved_search_embeddable.ts | 9 +- test/functional/apps/dashboard/share.ts | 10 +- .../apps/dashboard/url_field_formatter.ts | 10 +- test/functional/apps/dashboard/view_edit.ts | 7 +- .../dashboard/current/kibana/data.json | 3392 ----------------- .../dashboard/current/kibana/mappings.json | 476 --- .../dashboard/current/kibana.json | 2653 +++++++++++++ .../dashboard/current/kibana_unload.json | 35 + .../kbn_archiver/dashboard/legacy.json | 241 ++ .../test_suites/data_plugin/session.ts | 8 +- .../test_suites/panel_actions/index.js | 7 +- .../apps/canvas/embeddables/lens.ts | 3 +- .../apps/canvas/embeddables/maps.ts | 2 + .../apps/canvas/embeddables/saved_search.ts | 12 +- .../apps/canvas/embeddables/visualization.ts | 19 +- .../canvas/feature_controls/canvas_spaces.ts | 2 +- .../test/functional/apps/canvas/smoke_test.js | 2 +- .../apps/dashboard/dashboard_maps_by_value.ts | 4 +- .../apps/dashboard/dashboard_tagging.ts | 4 +- .../functional/apps/dashboard/panel_titles.ts | 2 +- .../reporting_and_security/usage.ts | 8 +- 48 files changed, 3206 insertions(+), 3959 deletions(-) delete mode 100644 test/functional/fixtures/es_archiver/dashboard/current/kibana/data.json delete mode 100644 test/functional/fixtures/es_archiver/dashboard/current/kibana/mappings.json create mode 100644 test/functional/fixtures/kbn_archiver/dashboard/current/kibana.json create mode 100644 test/functional/fixtures/kbn_archiver/dashboard/current/kibana_unload.json create mode 100644 test/functional/fixtures/kbn_archiver/dashboard/legacy.json diff --git a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts index 717f214211d95..02861fcb27fdb 100644 --- a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts +++ b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts @@ -219,6 +219,25 @@ export class KbnClientSavedObjects { this.log.success('deleted', deleted, 'objects'); } + public async cleanStandardList(options?: { space?: string }) { + // add types here + const types = [ + 'search', + 'index-pattern', + 'visualization', + 'dashboard', + 'lens', + 'map', + 'graph-workspace', + 'query', + 'tag', + 'url', + 'canvas-workpad', + ]; + const newOptions = { types, space: options?.space }; + await this.clean(newOptions); + } + public async bulkDelete(options: DeleteObjectsOptions) { let deleted = 0; let missing = 0; diff --git a/test/examples/embeddables/dashboard.ts b/test/examples/embeddables/dashboard.ts index 23f221c40d4da..6be6d833a8ddf 100644 --- a/test/examples/embeddables/dashboard.ts +++ b/test/examples/embeddables/dashboard.ts @@ -93,6 +93,7 @@ export const testDashboardInput = { // eslint-disable-next-line import/no-default-export export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); const pieChart = getService('pieChart'); const dashboardExpect = getService('dashboardExpect'); @@ -103,8 +104,9 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide describe('dashboard container', () => { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); - await esArchiver.loadIfNeeded( - 'test/functional/fixtures/es_archiver/dashboard/current/kibana' + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' ); await PageObjects.common.navigateToApp('dashboardEmbeddableExamples'); await testSubjects.click('dashboardEmbeddableByValue'); diff --git a/test/functional/apps/dashboard/copy_panel_to.ts b/test/functional/apps/dashboard/copy_panel_to.ts index 8877e5e47bd95..9a61b289ee1f3 100644 --- a/test/functional/apps/dashboard/copy_panel_to.ts +++ b/test/functional/apps/dashboard/copy_panel_to.ts @@ -14,7 +14,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardPanelActions = getService('dashboardPanelActions'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); - const esArchiver = getService('esArchiver'); const find = getService('find'); const PageObjects = getPageObjects([ @@ -40,7 +39,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard panel copy to', function viewEditModeTests() { before(async function () { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -61,6 +63,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async function () { await PageObjects.dashboard.gotoDashboardLandingPage(); + await kibanaServer.savedObjects.cleanStandardList(); }); it('does not show the new dashboard option when on a new dashboard', async () => { diff --git a/test/functional/apps/dashboard/create_and_add_embeddables.ts b/test/functional/apps/dashboard/create_and_add_embeddables.ts index 1390a7517cdbf..d8bf67b6d2873 100644 --- a/test/functional/apps/dashboard/create_and_add_embeddables.ts +++ b/test/functional/apps/dashboard/create_and_add_embeddables.ts @@ -16,13 +16,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); const browser = getService('browser'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardAddPanel = getService('dashboardAddPanel'); describe('create and add embeddables', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -37,6 +39,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardAddPanel.expectEditorMenuClosed(); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + describe('add new visualization link', () => { before(async () => { await PageObjects.common.navigateToApp('dashboard'); diff --git a/test/functional/apps/dashboard/dashboard_back_button.ts b/test/functional/apps/dashboard/dashboard_back_button.ts index 3b03ea525b903..d532444befdab 100644 --- a/test/functional/apps/dashboard/dashboard_back_button.ts +++ b/test/functional/apps/dashboard/dashboard_back_button.ts @@ -10,7 +10,6 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['dashboard', 'header', 'common', 'visualize', 'timePicker']); const browser = getService('browser'); @@ -18,8 +17,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard back button', () => { before(async () => { - await esArchiver.loadIfNeeded( - 'test/functional/fixtures/es_archiver/dashboard/current/kibana' + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' ); await security.testUser.setRoles(['kibana_admin', 'animals', 'test_logstash_reader']); await kibanaServer.uiSettings.replace({ @@ -31,6 +31,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await security.testUser.restoreDefaults(); + await kibanaServer.savedObjects.cleanStandardList(); }); it('after navigation from listing page to dashboard back button works', async () => { diff --git a/test/functional/apps/dashboard/dashboard_controls_integration.ts b/test/functional/apps/dashboard/dashboard_controls_integration.ts index 5ede80bf8eb8a..96a0bcdc0346b 100644 --- a/test/functional/apps/dashboard/dashboard_controls_integration.ts +++ b/test/functional/apps/dashboard/dashboard_controls_integration.ts @@ -16,7 +16,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const queryBar = getService('queryBar'); const pieChart = getService('pieChart'); const filterBar = getService('filterBar'); - const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); const dashboardAddPanel = getService('dashboardAddPanel'); @@ -30,7 +29,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Dashboard controls integration', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', @@ -44,6 +46,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await timePicker.setDefaultDataRange(); }); + after(async () => { + await security.testUser.restoreDefaults(); + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('shows the empty control callout on a new dashboard', async () => { await testSubjects.existOrFail('controls-empty'); }); diff --git a/test/functional/apps/dashboard/dashboard_error_handling.ts b/test/functional/apps/dashboard/dashboard_error_handling.ts index d120fb518f103..58304359458c7 100644 --- a/test/functional/apps/dashboard/dashboard_error_handling.ts +++ b/test/functional/apps/dashboard/dashboard_error_handling.ts @@ -10,7 +10,6 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['dashboard', 'header', 'common']); const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); @@ -22,8 +21,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { */ describe('dashboard error handling', () => { before(async () => { - await esArchiver.loadIfNeeded( - 'test/functional/fixtures/es_archiver/dashboard/current/kibana' + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); + // The kbn_archiver above was created from an es_archiver which intentionally had + // 2 missing index patterns. But that would fail to load with kbn_archiver. + // So we unload those 2 index patterns here. + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana_unload' ); await kibanaServer.importExport.load( 'test/functional/fixtures/kbn_archiver/dashboard_error_cases.json' @@ -31,6 +37,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToApp('dashboard'); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('correctly loads default index pattern on first load with an error embeddable', async () => { await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.loadSavedDashboard('Dashboard with Missing Lens Panel'); diff --git a/test/functional/apps/dashboard/dashboard_filter_bar.ts b/test/functional/apps/dashboard/dashboard_filter_bar.ts index 62eac2454ae96..a56143fa8b05e 100644 --- a/test/functional/apps/dashboard/dashboard_filter_bar.ts +++ b/test/functional/apps/dashboard/dashboard_filter_bar.ts @@ -17,7 +17,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const filterBar = getService('filterBar'); const pieChart = getService('pieChart'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const browser = getService('browser'); const security = getService('security'); @@ -32,7 +31,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard filter bar', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); + // The kbn_archiver above was created from an es_archiver which intentionally had + // 2 missing index patterns. But that would fail to load with kbn_archiver. + // So we unload those 2 index patterns here. + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana_unload' + ); await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', @@ -42,6 +50,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await security.testUser.restoreDefaults(); + await kibanaServer.savedObjects.cleanStandardList(); }); describe('Add a filter bar', function () { diff --git a/test/functional/apps/dashboard/dashboard_filtering.ts b/test/functional/apps/dashboard/dashboard_filtering.ts index 6c8a378831340..9522c47f907fc 100644 --- a/test/functional/apps/dashboard/dashboard_filtering.ts +++ b/test/functional/apps/dashboard/dashboard_filtering.ts @@ -22,7 +22,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const renderable = getService('renderable'); const testSubjects = getService('testSubjects'); const filterBar = getService('filterBar'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const security = getService('security'); const dashboardPanelActions = getService('dashboardPanelActions'); @@ -51,7 +50,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }; before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); + // The kbn_archiver above was created from an es_archiver which intentionally had + // 2 missing index patterns. But that would fail to load with kbn_archiver. + // So we unload those 2 index patterns here. + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana_unload' + ); await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', @@ -63,6 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await security.testUser.restoreDefaults(); + await kibanaServer.savedObjects.cleanStandardList(); }); describe('adding a filter that excludes all data', () => { diff --git a/test/functional/apps/dashboard/dashboard_grid.ts b/test/functional/apps/dashboard/dashboard_grid.ts index fecec34fd91e3..25e901fd25d8b 100644 --- a/test/functional/apps/dashboard/dashboard_grid.ts +++ b/test/functional/apps/dashboard/dashboard_grid.ts @@ -12,14 +12,16 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardPanelActions = getService('dashboardPanelActions'); const PageObjects = getPageObjects(['common', 'dashboard']); describe('dashboard grid', function () { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -29,6 +31,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.switchToEditMode(); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + describe('move panel', () => { // Specific test after https://github.com/elastic/kibana/issues/14764 fix it('Can move panel from bottom to top row', async () => { diff --git a/test/functional/apps/dashboard/dashboard_options.ts b/test/functional/apps/dashboard/dashboard_options.ts index 8080c6cb4cc7f..282674d0cec98 100644 --- a/test/functional/apps/dashboard/dashboard_options.ts +++ b/test/functional/apps/dashboard/dashboard_options.ts @@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'dashboard']); @@ -20,7 +19,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { let originalTitles: string[] = []; before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -31,6 +33,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { originalTitles = await PageObjects.dashboard.getPanelTitles(); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('should be able to hide all panel titles', async () => { await PageObjects.dashboard.checkHideTitle(); await retry.try(async () => { diff --git a/test/functional/apps/dashboard/dashboard_query_bar.ts b/test/functional/apps/dashboard/dashboard_query_bar.ts index 07fa3355c6fba..5092cadaf9d26 100644 --- a/test/functional/apps/dashboard/dashboard_query_bar.ts +++ b/test/functional/apps/dashboard/dashboard_query_bar.ts @@ -20,7 +20,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard query bar', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -29,6 +32,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.loadSavedDashboard('dashboard with filter'); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('causes panels to reload when refresh is clicked', async () => { await esArchiver.unload('test/functional/fixtures/es_archiver/dashboard/current/data'); diff --git a/test/functional/apps/dashboard/dashboard_saved_query.ts b/test/functional/apps/dashboard/dashboard_saved_query.ts index 015a00a713bdc..658afb9c641b2 100644 --- a/test/functional/apps/dashboard/dashboard_saved_query.ts +++ b/test/functional/apps/dashboard/dashboard_saved_query.ts @@ -11,7 +11,6 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['common', 'dashboard', 'timePicker']); const browser = getService('browser'); @@ -21,13 +20,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard saved queries', function describeIndexTests() { before(async function () { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); await PageObjects.common.navigateToApp('dashboard'); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + describe('saved query management component functionality', function () { before(async () => { await PageObjects.dashboard.gotoDashboardLandingPage(); diff --git a/test/functional/apps/dashboard/dashboard_snapshots.ts b/test/functional/apps/dashboard/dashboard_snapshots.ts index 9279bbd5806e7..9cb52c5dd5511 100644 --- a/test/functional/apps/dashboard/dashboard_snapshots.ts +++ b/test/functional/apps/dashboard/dashboard_snapshots.ts @@ -18,14 +18,16 @@ export default function ({ const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'common', 'timePicker']); const screenshot = getService('screenshots'); const browser = getService('browser'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardPanelActions = getService('dashboardPanelActions'); const dashboardAddPanel = getService('dashboardAddPanel'); describe('dashboard snapshots', function describeIndexTests() { before(async function () { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -38,6 +40,7 @@ export default function ({ after(async function () { await browser.setWindowSize(1300, 900); + await kibanaServer.savedObjects.cleanStandardList(); }); it('compare TSVB snapshot', async () => { diff --git a/test/functional/apps/dashboard/dashboard_unsaved_listing.ts b/test/functional/apps/dashboard/dashboard_unsaved_listing.ts index 2b2d96e8d723a..d9451eda5ff62 100644 --- a/test/functional/apps/dashboard/dashboard_unsaved_listing.ts +++ b/test/functional/apps/dashboard/dashboard_unsaved_listing.ts @@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardAddPanel = getService('dashboardAddPanel'); const dashboardPanelActions = getService('dashboardPanelActions'); @@ -36,7 +35,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }; before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -44,6 +46,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.preserveCrossAppState(); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('lists unsaved changes to existing dashboards', async () => { await PageObjects.dashboard.loadSavedDashboard(dashboardTitle); await PageObjects.dashboard.switchToEditMode(); diff --git a/test/functional/apps/dashboard/dashboard_unsaved_state.ts b/test/functional/apps/dashboard/dashboard_unsaved_state.ts index c2da82a96cd0c..5afe3b9937433 100644 --- a/test/functional/apps/dashboard/dashboard_unsaved_state.ts +++ b/test/functional/apps/dashboard/dashboard_unsaved_state.ts @@ -15,7 +15,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const queryBar = getService('queryBar'); const filterBar = getService('filterBar'); - const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); const dashboardAddPanel = getService('dashboardAddPanel'); @@ -27,7 +26,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // FLAKY https://github.com/elastic/kibana/issues/112812 describe.skip('dashboard unsaved state', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -39,6 +41,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { originalPanelCount = await PageObjects.dashboard.getPanelCount(); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + describe('view mode state', () => { before(async () => { await queryBar.setQuery(testQuery); diff --git a/test/functional/apps/dashboard/data_shared_attributes.ts b/test/functional/apps/dashboard/data_shared_attributes.ts index 4b993287ffe42..a94cf1b6063a9 100644 --- a/test/functional/apps/dashboard/data_shared_attributes.ts +++ b/test/functional/apps/dashboard/data_shared_attributes.ts @@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardPanelActions = getService('dashboardPanelActions'); const security = getService('security'); @@ -22,7 +21,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { let originalPanelTitles: string[]; before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', @@ -35,6 +37,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await security.testUser.restoreDefaults(); + await kibanaServer.savedObjects.cleanStandardList(); }); it('should have time picker with data-shared-timefilter-duration', async () => { diff --git a/test/functional/apps/dashboard/edit_embeddable_redirects.ts b/test/functional/apps/dashboard/edit_embeddable_redirects.ts index 02f178a5153a2..763488cc21ab1 100644 --- a/test/functional/apps/dashboard/edit_embeddable_redirects.ts +++ b/test/functional/apps/dashboard/edit_embeddable_redirects.ts @@ -12,14 +12,16 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardPanelActions = getService('dashboardPanelActions'); const dashboardAddPanel = getService('dashboardAddPanel'); describe('edit embeddable redirects', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -29,6 +31,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.switchToEditMode(); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('redirects via save and return button after edit', async () => { await dashboardPanelActions.openContextMenu(); await dashboardPanelActions.clickEdit(); diff --git a/test/functional/apps/dashboard/edit_visualizations.js b/test/functional/apps/dashboard/edit_visualizations.js index 507d4b8308d4c..d4de54586b731 100644 --- a/test/functional/apps/dashboard/edit_visualizations.js +++ b/test/functional/apps/dashboard/edit_visualizations.js @@ -10,7 +10,6 @@ import expect from '@kbn/expect'; export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'common', 'visEditor']); - const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const appsMenu = getService('appsMenu'); const kibanaServer = getService('kibanaServer'); @@ -44,13 +43,20 @@ export default function ({ getService, getPageObjects }) { describe('edit visualizations from dashboard', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); await PageObjects.common.navigateToApp('dashboard'); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('save button returns to dashboard after editing visualization with changes saved', async () => { const title = 'test save'; await PageObjects.dashboard.gotoDashboardLandingPage(); diff --git a/test/functional/apps/dashboard/embed_mode.ts b/test/functional/apps/dashboard/embed_mode.ts index 943a6b3bdb469..7e53bff7387ca 100644 --- a/test/functional/apps/dashboard/embed_mode.ts +++ b/test/functional/apps/dashboard/embed_mode.ts @@ -13,7 +13,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['dashboard', 'common']); const browser = getService('browser'); @@ -28,7 +27,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ]; before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -81,6 +83,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Then get rid of the timestamp so the rest of the tests work with state and app switching. useTimeStamp = false; await browser.get(newUrl.toString(), useTimeStamp); + await kibanaServer.savedObjects.cleanStandardList(); }); }); } diff --git a/test/functional/apps/dashboard/embeddable_data_grid.ts b/test/functional/apps/dashboard/embeddable_data_grid.ts index 5dea22f5006c5..060c467656662 100644 --- a/test/functional/apps/dashboard/embeddable_data_grid.ts +++ b/test/functional/apps/dashboard/embeddable_data_grid.ts @@ -23,8 +23,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); - await esArchiver.loadIfNeeded( - 'test/functional/fixtures/es_archiver/dashboard/current/kibana' + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', @@ -39,6 +40,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); }); + after(async function () { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('should expand the detail row when the toggle arrow is clicked', async function () { await retry.try(async function () { await dataGrid.clickRowToggle({ isAnchorRow: false, rowIndex: 0 }); diff --git a/test/functional/apps/dashboard/embeddable_library.ts b/test/functional/apps/dashboard/embeddable_library.ts index fd1aa0d91def7..2abf75f6385ac 100644 --- a/test/functional/apps/dashboard/embeddable_library.ts +++ b/test/functional/apps/dashboard/embeddable_library.ts @@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); - const esArchiver = getService('esArchiver'); const find = getService('find'); const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); @@ -21,7 +20,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('embeddable library', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); diff --git a/test/functional/apps/dashboard/embeddable_rendering.ts b/test/functional/apps/dashboard/embeddable_rendering.ts index 8fa2337eef0cd..840826be46532 100644 --- a/test/functional/apps/dashboard/embeddable_rendering.ts +++ b/test/functional/apps/dashboard/embeddable_rendering.ts @@ -21,7 +21,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const browser = getService('browser'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const pieChart = getService('pieChart'); const elasticChart = getService('elasticChart'); @@ -103,7 +102,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard embeddable rendering', function describeIndexTests() { before(async () => { await security.testUser.setRoles(['kibana_admin', 'animals', 'test_logstash_reader']); - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -122,12 +124,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const newUrl = currentUrl.replace(/\?.*$/, ''); await browser.get(newUrl, false); await security.testUser.restoreDefaults(); + await kibanaServer.savedObjects.cleanStandardList(); }); it('adding visualizations', async () => { await elasticChart.setNewChartUiDebugFlag(true); visNames = await dashboardAddPanel.addEveryVisualization('"Rendering Test"'); + expect(visNames.length).to.be.equal(24); await dashboardExpect.visualizationsArePresent(visNames); // This one is rendered via svg which lets us do better testing of what is being rendered. diff --git a/test/functional/apps/dashboard/empty_dashboard.ts b/test/functional/apps/dashboard/empty_dashboard.ts index 46a8545b79596..a7524eaa94b8a 100644 --- a/test/functional/apps/dashboard/empty_dashboard.ts +++ b/test/functional/apps/dashboard/empty_dashboard.ts @@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardAddPanel = getService('dashboardAddPanel'); const dashboardVisualizations = getService('dashboardVisualizations'); @@ -21,7 +20,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('empty dashboard', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -33,6 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await dashboardAddPanel.closeAddPanel(); await PageObjects.dashboard.gotoDashboardLandingPage(); + await kibanaServer.savedObjects.cleanStandardList(); }); it('should display empty widget', async () => { diff --git a/test/functional/apps/dashboard/full_screen_mode.ts b/test/functional/apps/dashboard/full_screen_mode.ts index fcfd0fc49dd2b..74fa2168a1461 100644 --- a/test/functional/apps/dashboard/full_screen_mode.ts +++ b/test/functional/apps/dashboard/full_screen_mode.ts @@ -13,7 +13,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const browser = getService('browser'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardPanelActions = getService('dashboardPanelActions'); const PageObjects = getPageObjects(['dashboard', 'common']); @@ -21,7 +20,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('full screen mode', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -30,6 +32,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.loadSavedDashboard('few panels'); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('option not available in edit mode', async () => { await PageObjects.dashboard.switchToEditMode(); const exists = await PageObjects.dashboard.fullScreenModeMenuItemExists(); diff --git a/test/functional/apps/dashboard/legacy_urls.ts b/test/functional/apps/dashboard/legacy_urls.ts index b449c0f6728a5..1e4138e63d393 100644 --- a/test/functional/apps/dashboard/legacy_urls.ts +++ b/test/functional/apps/dashboard/legacy_urls.ts @@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const dashboardAddPanel = getService('dashboardAddPanel'); const listingTable = getService('listingTable'); - const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const security = getService('security'); let kibanaLegacyBaseUrl: string; @@ -33,7 +33,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('legacy urls', function describeIndexTests() { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); await dashboardAddPanel.addVisualization('Rendering-Test:-animal-sounds-pie'); diff --git a/test/functional/apps/dashboard/panel_expand_toggle.ts b/test/functional/apps/dashboard/panel_expand_toggle.ts index 00500450595eb..272ec3824e233 100644 --- a/test/functional/apps/dashboard/panel_expand_toggle.ts +++ b/test/functional/apps/dashboard/panel_expand_toggle.ts @@ -12,14 +12,16 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardPanelActions = getService('dashboardPanelActions'); const PageObjects = getPageObjects(['dashboard', 'visualize', 'header', 'common']); describe('expanding a panel', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -28,6 +30,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.loadSavedDashboard('few panels'); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('hides other panels', async () => { await dashboardPanelActions.openContextMenu(); await dashboardPanelActions.clickExpandPanelToggle(); diff --git a/test/functional/apps/dashboard/saved_search_embeddable.ts b/test/functional/apps/dashboard/saved_search_embeddable.ts index b08dc43210d26..02050eec30227 100644 --- a/test/functional/apps/dashboard/saved_search_embeddable.ts +++ b/test/functional/apps/dashboard/saved_search_embeddable.ts @@ -22,8 +22,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); - await esArchiver.loadIfNeeded( - 'test/functional/fixtures/es_archiver/dashboard/current/kibana' + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', @@ -38,6 +39,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('highlighting on filtering works', async function () { await dashboardAddPanel.addSavedSearch('Rendering-Test:-saved-search'); await filterBar.addFilter('agent', 'is', 'Mozilla'); diff --git a/test/functional/apps/dashboard/share.ts b/test/functional/apps/dashboard/share.ts index 77a858b22ec79..7fe8048ab7c04 100644 --- a/test/functional/apps/dashboard/share.ts +++ b/test/functional/apps/dashboard/share.ts @@ -10,13 +10,15 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['dashboard', 'common', 'share']); describe('share dashboard', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -25,6 +27,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.loadSavedDashboard('few panels'); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('has "panels" state when sharing a snapshot', async () => { await PageObjects.share.clickShareTopNavButton(); const sharedUrl = await PageObjects.share.getSharedUrl(); diff --git a/test/functional/apps/dashboard/url_field_formatter.ts b/test/functional/apps/dashboard/url_field_formatter.ts index 16cdb62768219..be480066c0365 100644 --- a/test/functional/apps/dashboard/url_field_formatter.ts +++ b/test/functional/apps/dashboard/url_field_formatter.ts @@ -18,7 +18,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'timePicker', 'visChart', ]); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); const browser = getService('browser'); @@ -38,7 +37,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Changing field formatter to Url', () => { before(async function () { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -52,6 +54,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await settings.controlChangeSave(); }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + it('applied on dashboard', async () => { await common.navigateToApp('dashboard'); await dashboard.loadSavedDashboard('dashboard with table'); diff --git a/test/functional/apps/dashboard/view_edit.ts b/test/functional/apps/dashboard/view_edit.ts index f0ee5aad7a7cf..a73924a8ae75f 100644 --- a/test/functional/apps/dashboard/view_edit.ts +++ b/test/functional/apps/dashboard/view_edit.ts @@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const queryBar = getService('queryBar'); - const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardAddPanel = getService('dashboardAddPanel'); const PageObjects = getPageObjects(['dashboard', 'header', 'common', 'visualize', 'timePicker']); @@ -22,7 +21,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard view edit mode', function viewEditModeTests() { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', @@ -33,6 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await security.testUser.restoreDefaults(); + await kibanaServer.savedObjects.cleanStandardList(); }); it('create new dashboard opens in edit mode', async function () { diff --git a/test/functional/fixtures/es_archiver/dashboard/current/kibana/data.json b/test/functional/fixtures/es_archiver/dashboard/current/kibana/data.json deleted file mode 100644 index eef30ceb606ed..0000000000000 --- a/test/functional/fixtures/es_archiver/dashboard/current/kibana/data.json +++ /dev/null @@ -1,3392 +0,0 @@ -{ - "type": "doc", - "value": { - "id": "search:a16d1990-3dca-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "search": "7.9.3" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "search": { - "columns": [ - "animal", - "isDog", - "name", - "sound", - "weightLbs" - ], - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"weightLbs:>40\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "sort": [ - [ - "weightLbs", - "desc" - ] - ], - "title": "animal weights", - "version": 1 - }, - "type": "search", - "updated_at": "2018-04-11T20:55:26.317Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "config:6.3.0", - "index": ".kibana", - "source": { - "config": { - "buildNum": 8467, - "defaultIndex": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c" - }, - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "config": "7.13.0" - }, - "references": [ - ], - "type": "config", - "updated_at": "2018-04-11T20:43:55.434Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:61c58ad0-3dd3-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":0,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"}]", - "refreshInterval": { - "display": "Off", - "pause": false, - "value": 0 - }, - "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", - "timeRestore": true, - "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", - "title": "dashboard with filter", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - }, - { - "id": "50643b60-3dd3-11e8-b2b9-5d5dc1715159", - "name": "1:panel_1", - "type": "visualization" - }, - { - "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", - "name": "2:panel_2", - "type": "search" - } - ], - "type": "dashboard", - "updated_at": "2018-04-11T21:57:52.253Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:2ae34a60-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":0,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"}]", - "timeRestore": false, - "title": "couple panels", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "145ced90-3dcb-11e8-8660-4d65aa086b3c", - "name": "1:panel_1", - "type": "visualization" - }, - { - "id": "e2023110-3dcb-11e8-8660-4d65aa086b3c", - "name": "2:panel_2", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:03:29.670Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:76d03330-3dd3-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "and_descriptions_has_underscores", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "dashboard_with_underscores", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T21:58:27.555Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:9b780cd0-3dd3-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "* hi & $%!!@# 漢字 ^--=++[]{};'~`~<>?,./:\";'\\|\\\\ special chars", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:00:07.322Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:6c0b16e0-3dd3-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "dashboard-name-has-dashes", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T21:58:09.486Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:19523860-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "im empty too", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:03:00.198Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:14616b50-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "im empty", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:02:51.909Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:33bb8ad0-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"}]", - "timeRestore": false, - "title": "few panels", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "145ced90-3dcb-11e8-8660-4d65aa086b3c", - "name": "1:panel_1", - "type": "visualization" - }, - { - "id": "e2023110-3dcb-11e8-8660-4d65aa086b3c", - "name": "2:panel_2", - "type": "visualization" - }, - { - "id": "4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", - "name": "3:panel_3", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:03:44.509Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:60659030-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "zz 2", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:04:59.443Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:65227c00-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "zz 3", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:05:07.392Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:6803a2f0-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "zz 4", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:05:12.223Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:6b18f940-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "zz 5", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:05:17.396Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:6e12ff60-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "zz 6", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:05:22.390Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:4f0fd980-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "zz", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:04:30.360Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:3de0bda0-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "1", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:04:01.530Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:46c8b580-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "2", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:04:16.472Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:708fe640-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "zz 7", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:05:26.564Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:7b8d50a0-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "Hi i have a lot of words in my dashboard name! It's pretty long i wonder what it'll look like", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:05:45.002Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:7e42d3b0-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "bye", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:05:49.547Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:846988b0-3dd4-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[]", - "timeRestore": false, - "title": "last", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - ], - "type": "dashboard", - "updated_at": "2018-04-11T22:05:59.867Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:cbd3bc30-3e5a-11e8-9fc3-39e49624228e", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"weightLbs:<50\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"name.keyword\",\"value\":\"Fee Fee\",\"params\":{\"query\":\"Fee Fee\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"name.keyword\":{\"query\":\"Fee Fee\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":true,\"useMargins\":true,\"hidePanelTitles\":true}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"}]", - "timeRestore": false, - "title": "bug", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - }, - { - "id": "771b4f10-3e59-11e8-9fc3-39e49624228e", - "name": "1:panel_1", - "type": "visualization" - }, - { - "id": "befdb6b0-3e59-11e8-9fc3-39e49624228e", - "name": "2:panel_2", - "type": "visualization" - }, - { - "id": "4c0c3f90-3e5a-11e8-9fc3-39e49624228e", - "name": "3:panel_3", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2018-04-12T14:07:12.243Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:5bac3a80-3e5b-11e8-9fc3-39e49624228e", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "dashboard with scripted filter, negated filter and query", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"weightLbs:<50\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"name.keyword\",\"negate\":true,\"params\":{\"query\":\"Fee Fee\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"Fee Fee\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"name.keyword\":{\"query\":\"Fee Fee\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":\"is dog\",\"disabled\":false,\"field\":\"isDog\",\"key\":\"isDog\",\"negate\":false,\"params\":{\"value\":true},\"type\":\"phrase\",\"value\":\"true\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"script\":{\"script\":{\"inline\":\"boolean compare(Supplier s, def v) {return s.get() == v;}compare(() -> { return doc['animal.keyword'].value == 'dog' }, params.value);\",\"lang\":\"painless\",\"params\":{\"value\":true}}}}],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":true,\"hidePanelTitles\":false,\"useMargins\":true}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"}]", - "refreshInterval": { - "display": "Off", - "pause": false, - "section": 0, - "value": 0 - }, - "timeFrom": "Wed Apr 12 2017 10:06:21 GMT-0400", - "timeRestore": true, - "timeTo": "Thu Apr 12 2018 10:06:21 GMT-0400", - "title": "filters", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - }, - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "type": "index-pattern" - }, - { - "id": "771b4f10-3e59-11e8-9fc3-39e49624228e", - "name": "1:panel_1", - "type": "visualization" - }, - { - "id": "4c0c3f90-3e5a-11e8-9fc3-39e49624228e", - "name": "3:panel_3", - "type": "visualization" - }, - { - "id": "50643b60-3dd3-11e8-b2b9-5d5dc1715159", - "name": "4:panel_4", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2018-04-12T14:11:13.576Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "index-pattern:f908c8e0-3e6d-11e8-bbb9-e15942d5d48c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "index-pattern": { - "fields": "[{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"activity level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"barking level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"breed\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"breed.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"size.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"trainability\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", - "title": "dogbreeds" - }, - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [ - ], - "type": "index-pattern", - "updated_at": "2018-04-12T16:24:29.357Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:5e085850-3e6e-11e8-bbb9-e15942d5d48c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "f908c8e0-3e6d-11e8-bbb9-e15942d5d48c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-12T16:27:17.973Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "non timebased line chart - dog data", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"non timebased line chart - dog data\",\"type\":\"line\",\"params\":{\"type\":\"line\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Max trainability\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"line\",\"mode\":\"normal\",\"data\":{\"label\":\"Max trainability\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true},{\"show\":true,\"mode\":\"normal\",\"type\":\"line\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"3\",\"label\":\"Max barking level\"},\"valueAxis\":\"ValueAxis-1\"},{\"show\":true,\"mode\":\"normal\",\"type\":\"line\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"4\",\"label\":\"Max activity level\"},\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"trainability\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"breed.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"barking level\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"activity level\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:a5d56330-3e6e-11e8-bbb9-e15942d5d48c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "I have two visualizations that are created off a non time based index", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":0,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"}]", - "timeRestore": false, - "title": "Non time based", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "5e085850-3e6e-11e8-bbb9-e15942d5d48c", - "name": "1:panel_1", - "type": "visualization" - }, - { - "id": "8bc8d6c0-3e6e-11e8-bbb9-e15942d5d48c", - "name": "2:panel_2", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2018-04-12T16:29:18.435Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:d2525040-3dcd-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "I have one of every visualization type since the last time I was created!", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"6\"},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"8\"},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"9\"},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"11\"},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"12\"},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"13\"},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":105,\"w\":24,\"h\":15,\"i\":\"15\"},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":105,\"w\":24,\"h\":15,\"i\":\"16\"},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":120,\"w\":24,\"h\":15,\"i\":\"17\"},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":120,\"w\":24,\"h\":15,\"i\":\"18\"},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":135,\"w\":24,\"h\":15,\"i\":\"19\"},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":135,\"w\":24,\"h\":15,\"i\":\"20\"},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":150,\"w\":24,\"h\":15,\"i\":\"21\"},\"panelIndex\":\"21\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_21\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":150,\"w\":24,\"h\":15,\"i\":\"22\"},\"panelIndex\":\"22\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_22\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":165,\"w\":24,\"h\":15,\"i\":\"23\"},\"panelIndex\":\"23\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_23\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"x\":24,\"y\":165,\"w\":24,\"h\":15,\"i\":\"24\"},\"panelIndex\":\"24\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_24\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":180,\"w\":24,\"h\":15,\"i\":\"25\"},\"panelIndex\":\"25\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_25\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"x\":24,\"y\":180,\"w\":24,\"h\":15,\"i\":\"26\"},\"panelIndex\":\"26\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_26\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":195,\"w\":24,\"h\":15,\"i\":\"27\"},\"panelIndex\":\"27\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_27\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":195,\"w\":24,\"h\":15,\"i\":\"28\"},\"panelIndex\":\"28\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_28\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":210,\"w\":24,\"h\":15,\"i\":\"29\"},\"panelIndex\":\"29\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_29\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":210,\"i\":\"30\"},\"panelIndex\":\"30\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_30\"}]", - "refreshInterval": { - "display": "Off", - "pause": false, - "value": 0 - }, - "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", - "timeRestore": true, - "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", - "title": "dashboard with everything", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "e6140540-3dca-11e8-8660-4d65aa086b3c", - "name": "1:panel_1", - "type": "visualization" - }, - { - "id": "3525b840-3dcb-11e8-8660-4d65aa086b3c", - "name": "2:panel_2", - "type": "visualization" - }, - { - "id": "4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", - "name": "3:panel_3", - "type": "visualization" - }, - { - "id": "ffa2e0c0-3dcb-11e8-8660-4d65aa086b3c", - "name": "5:panel_5", - "type": "visualization" - }, - { - "id": "e2023110-3dcb-11e8-8660-4d65aa086b3c", - "name": "6:panel_6", - "type": "visualization" - }, - { - "id": "145ced90-3dcb-11e8-8660-4d65aa086b3c", - "name": "7:panel_7", - "type": "visualization" - }, - { - "id": "2d1b1620-3dcd-11e8-8660-4d65aa086b3c", - "name": "8:panel_8", - "type": "visualization" - }, - { - "id": "42535e30-3dcd-11e8-8660-4d65aa086b3c", - "name": "9:panel_9", - "type": "visualization" - }, - { - "id": "42535e30-3dcd-11e8-8660-4d65aa086b3c", - "name": "10:panel_10", - "type": "visualization" - }, - { - "id": "4c0f47e0-3dcd-11e8-8660-4d65aa086b3c", - "name": "11:panel_11", - "type": "visualization" - }, - { - "id": "11ae2bd0-3dcc-11e8-8660-4d65aa086b3c", - "name": "12:panel_12", - "type": "visualization" - }, - { - "id": "3fe22200-3dcb-11e8-8660-4d65aa086b3c", - "name": "13:panel_13", - "type": "visualization" - }, - { - "id": "78803be0-3dcd-11e8-8660-4d65aa086b3c", - "name": "15:panel_15", - "type": "visualization" - }, - { - "id": "b92ae920-3dcc-11e8-8660-4d65aa086b3c", - "name": "16:panel_16", - "type": "visualization" - }, - { - "id": "e4d8b430-3dcc-11e8-8660-4d65aa086b3c", - "name": "17:panel_17", - "type": "visualization" - }, - { - "id": "f81134a0-3dcc-11e8-8660-4d65aa086b3c", - "name": "18:panel_18", - "type": "visualization" - }, - { - "id": "cc43fab0-3dcc-11e8-8660-4d65aa086b3c", - "name": "19:panel_19", - "type": "visualization" - }, - { - "id": "02a2e4e0-3dcd-11e8-8660-4d65aa086b3c", - "name": "20:panel_20", - "type": "visualization" - }, - { - "id": "df815d20-3dcc-11e8-8660-4d65aa086b3c", - "name": "21:panel_21", - "type": "visualization" - }, - { - "id": "c40f4d40-3dcc-11e8-8660-4d65aa086b3c", - "name": "22:panel_22", - "type": "visualization" - }, - { - "id": "7fda8ee0-3dcd-11e8-8660-4d65aa086b3c", - "name": "23:panel_23", - "type": "visualization" - }, - { - "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", - "name": "24:panel_24", - "type": "search" - }, - { - "id": "be5accf0-3dca-11e8-8660-4d65aa086b3c", - "name": "25:panel_25", - "type": "search" - }, - { - "id": "ca5ada40-3dca-11e8-8660-4d65aa086b3c", - "name": "26:panel_26", - "type": "search" - }, - { - "id": "771b4f10-3e59-11e8-9fc3-39e49624228e", - "name": "27:panel_27", - "type": "visualization" - }, - { - "id": "5e085850-3e6e-11e8-bbb9-e15942d5d48c", - "name": "28:panel_28", - "type": "visualization" - }, - { - "id": "8bc8d6c0-3e6e-11e8-bbb9-e15942d5d48c", - "name": "29:panel_29", - "type": "visualization" - }, - { - "id": "befdb6b0-3e59-11e8-9fc3-39e49624228e", - "name": "30:panel_30", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2018-04-16T16:05:02.915Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:d2525040-3dcd-11e8-8660-4d65aa086b3b", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "I have one of every visualization type since the last time I was created!", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"}]", - "refreshInterval": { - "display": "Off", - "pause": false, - "value": 0 - }, - "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", - "timeRestore": true, - "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", - "title": "dashboard with table", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", - "name": "3:panel_3", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2018-04-16T16:05:02.915Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:29bd0240-4197-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-16T16:56:53.092Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.src\",\"value\":\"CN\",\"params\":{\"query\":\"CN\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.src\":{\"query\":\"CN\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"bytes >= 10000\",\"language\":\"kuery\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Kuery: pie bytes with kuery and filter", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Kuery: pie bytes with kuery and filter\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"bytes\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "index-pattern:0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "index-pattern": { - "fieldFormatMap": "{\"machine.ram\":{\"id\":\"number\",\"params\":{\"pattern\":\"0,0.[000] b\"}}}", - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", - "timeFieldName": "@timestamp", - "title": "logstash-*" - }, - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [ - ], - "type": "index-pattern", - "updated_at": "2018-04-16T16:57:12.263Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "search:55d37a30-4197-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "search": "7.9.3" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "search": { - "columns": [ - "agent", - "bytes", - "clientip" - ], - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"clientip : 73.14.212.83\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"range\",\"key\":\"bytes\",\"value\":\"100 to 1,000\",\"params\":{\"gte\":100,\"lt\":1000},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"range\":{\"bytes\":{\"gte\":100,\"lt\":1000}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "sort": [ - [ - "@timestamp", - "desc" - ] - ], - "title": "Bytes and kuery in saved search with filter", - "version": 1 - }, - "type": "search", - "updated_at": "2018-04-16T16:58:07.059Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:b60de070-4197-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "Bytes bytes and more bytes", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":15,\"w\":17,\"h\":8,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":18,\"h\":13,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":37,\"w\":24,\"h\":12,\"i\":\"6\"},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":18,\"y\":30,\"w\":9,\"h\":7,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":28,\"y\":23,\"w\":15,\"h\":13,\"i\":\"8\"},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":43,\"w\":24,\"h\":15,\"i\":\"9\"},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":49,\"w\":18,\"h\":12,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":58,\"w\":24,\"h\":15,\"i\":\"11\"},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":61,\"w\":5,\"h\":4,\"i\":\"12\"},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":73,\"w\":17,\"h\":6,\"i\":\"13\"},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":65,\"w\":24,\"h\":15,\"i\":\"14\"},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":79,\"w\":24,\"h\":6,\"i\":\"15\"},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":80,\"w\":24,\"h\":15,\"i\":\"16\"},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":85,\"w\":13,\"h\":11,\"i\":\"17\"},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"x\":24,\"y\":95,\"w\":23,\"h\":11,\"i\":\"18\"},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"}]", - "refreshInterval": { - "display": "Off", - "pause": false, - "value": 0 - }, - "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", - "timeRestore": true, - "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", - "title": "All about those bytes", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "7ff2c4c0-4191-11e8-bb13-d53698fb349a", - "name": "1:panel_1", - "type": "visualization" - }, - { - "id": "03d2afd0-4192-11e8-bb13-d53698fb349a", - "name": "2:panel_2", - "type": "visualization" - }, - { - "id": "63983430-4192-11e8-bb13-d53698fb349a", - "name": "3:panel_3", - "type": "visualization" - }, - { - "id": "0ca8c600-4195-11e8-bb13-d53698fb349a", - "name": "4:panel_4", - "type": "visualization" - }, - { - "id": "c10c6b00-4191-11e8-bb13-d53698fb349a", - "name": "5:panel_5", - "type": "visualization" - }, - { - "id": "760a9060-4190-11e8-bb13-d53698fb349a", - "name": "6:panel_6", - "type": "visualization" - }, - { - "id": "1dcdfe30-4192-11e8-bb13-d53698fb349a", - "name": "7:panel_7", - "type": "visualization" - }, - { - "id": "584c0300-4191-11e8-bb13-d53698fb349a", - "name": "8:panel_8", - "type": "visualization" - }, - { - "id": "b3e70d00-4190-11e8-bb13-d53698fb349a", - "name": "9:panel_9", - "type": "visualization" - }, - { - "id": "df72ad40-4194-11e8-bb13-d53698fb349a", - "name": "10:panel_10", - "type": "visualization" - }, - { - "id": "9bebe980-4192-11e8-bb13-d53698fb349a", - "name": "11:panel_11", - "type": "visualization" - }, - { - "id": "9fb4c670-4194-11e8-bb13-d53698fb349a", - "name": "12:panel_12", - "type": "visualization" - }, - { - "id": "35417e50-4194-11e8-bb13-d53698fb349a", - "name": "13:panel_13", - "type": "visualization" - }, - { - "id": "039e4770-4194-11e8-bb13-d53698fb349a", - "name": "14:panel_14", - "type": "visualization" - }, - { - "id": "76c7f020-4194-11e8-bb13-d53698fb349a", - "name": "15:panel_15", - "type": "visualization" - }, - { - "id": "8090dcb0-4195-11e8-bb13-d53698fb349a", - "name": "16:panel_16", - "type": "visualization" - }, - { - "id": "29bd0240-4197-11e8-bb13-d53698fb349a", - "name": "17:panel_17", - "type": "visualization" - }, - { - "id": "55d37a30-4197-11e8-bb13-d53698fb349a", - "name": "18:panel_18", - "type": "search" - } - ], - "type": "dashboard", - "updated_at": "2018-04-16T17:00:48.503Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:78803be0-3dcd-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:32.127Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: tag cloud", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: tag cloud\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:3fe22200-3dcb-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:32.130Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: pie", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"bytes\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:11ae2bd0-3dcc-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:32.133Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: metric", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: metric\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:145ced90-3dcb-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:32.134Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: heatmap", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 15\":\"rgb(247,252,245)\",\"15 - 30\":\"rgb(199,233,192)\",\"30 - 45\":\"rgb(116,196,118)\",\"45 - 60\":\"rgb(35,139,69)\"}}}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: heatmap\",\"type\":\"heatmap\",\"params\":{\"type\":\"heatmap\",\"addTooltip\":true,\"addLegend\":true,\"enableHover\":false,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Greens\",\"setColorRange\":false,\"colorsRange\":[],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"overwriteColor\":false,\"color\":\"#555\"}}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"bytes\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:e2023110-3dcb-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:32.135Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: guage", - "uiStateJSON": "{\"vis\":{\"colors\":{\"0 - 50000\":\"#EF843C\",\"75000 - 10000000\":\"#3F6833\"},\"defaultColors\":{\"0 - 5000000\":\"rgb(0,104,55)\",\"50000000 - 74998990099\":\"rgb(165,0,38)\"}}}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: guage\",\"type\":\"gauge\",\"params\":{\"addLegend\":true,\"addTooltip\":true,\"gauge\":{\"backStyle\":\"Full\",\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":5000000},{\"from\":50000000,\"to\":74998990099}],\"extendRange\":true,\"gaugeColorMode\":\"Labels\",\"gaugeStyle\":\"Full\",\"gaugeType\":\"Arc\",\"invertColors\":false,\"labels\":{\"color\":\"black\",\"show\":true},\"orientation\":\"vertical\",\"percentageMode\":false,\"scale\":{\"color\":\"#333\",\"labels\":false,\"show\":true},\"style\":{\"bgColor\":false,\"bgFill\":\"#eee\",\"bgMask\":false,\"bgWidth\":0.9,\"fontSize\":60,\"labelColor\":true,\"mask\":false,\"maskBars\":50,\"subText\":\"\",\"width\":0.9},\"type\":\"meter\",\"alignment\":\"horizontal\"},\"isDisplayWarning\":false,\"type\":\"gauge\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"machine.ram\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:b92ae920-3dcc-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:31.110Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: timelion", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: timelion\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(*, metric=avg:bytes, split=ip:5)\",\"interval\":\"auto\"},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:e4d8b430-3dcc-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:31.106Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: tsvb-guage", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: tsvb-guage\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"gauge\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"d18e5970-3dcc-11e8-a2f6-c162ca6cf6ea\",\"color\":\"rgba(160,70,216,1)\",\"split_mode\":\"filter\",\"metrics\":[{\"id\":\"d18e5971-3dcc-11e8-a2f6-c162ca6cf6ea\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"bar_color_rules\":[{\"id\":\"cd25a820-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_color_rules\":[{\"id\":\"e0be22e0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_width\":10,\"gauge_inner_width\":10,\"gauge_style\":\"half\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:4c0f47e0-3dcd-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:31.111Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: markdown", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: markdown\",\"type\":\"markdown\",\"params\":{\"fontSize\":20,\"openLinksInNewTab\":false,\"markdown\":\"I'm a markdown!\"},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:2d1b1620-3dcd-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "control_0_index_pattern", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "control_1_index_pattern", - "type": "index-pattern" - }, - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "control_2_index_pattern", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:31.123Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: input control", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: input control\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1523481142694\",\"fieldName\":\"bytes\",\"parent\":\"\",\"label\":\"Bytes Input List\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1523481163654\",\"fieldName\":\"bytes\",\"parent\":\"\",\"label\":\"Bytes range\",\"type\":\"range\",\"options\":{\"decimalPlaces\":0,\"step\":1},\"indexPatternRefName\":\"control_1_index_pattern\"},{\"id\":\"1523481176519\",\"fieldName\":\"sound.keyword\",\"parent\":\"\",\"label\":\"Animal sounds\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_2_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:8bc8d6c0-3e6e-11e8-bbb9-e15942d5d48c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "f908c8e0-3e6d-11e8-bbb9-e15942d5d48c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "f908c8e0-3e6d-11e8-bbb9-e15942d5d48c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:31.173Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"size.keyword\",\"value\":\"extra large\",\"params\":{\"query\":\"extra large\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"size.keyword\":{\"query\":\"extra large\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: non timebased line chart - dog data - with filter", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"field\":\"trainability\"},\"schema\":\"metric\",\"type\":\"max\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"breed.keyword\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"barking level\"},\"schema\":\"metric\",\"type\":\"max\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"field\":\"activity level\"},\"schema\":\"metric\",\"type\":\"max\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Max trainability\"},\"drawLinesBetweenPoints\":true,\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"},{\"data\":{\"id\":\"3\",\"label\":\"Max barking level\"},\"drawLinesBetweenPoints\":true,\"mode\":\"normal\",\"show\":true,\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"},{\"data\":{\"id\":\"4\",\"label\":\"Max activity level\"},\"drawLinesBetweenPoints\":true,\"mode\":\"normal\",\"show\":true,\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"times\":[],\"type\":\"line\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Max trainability\"},\"type\":\"value\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"title\":\"Rendering Test: non timebased line chart - dog data - with filter\",\"type\":\"line\"}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:42535e30-3dcd-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "control_0_index_pattern", - "type": "index-pattern" - }, - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "control_1_index_pattern", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:31.124Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: input control parent", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: input control parent\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1523481216736\",\"fieldName\":\"animal.keyword\",\"parent\":\"\",\"label\":\"Animal type\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1523481176519\",\"fieldName\":\"sound.keyword\",\"parent\":\"1523481216736\",\"label\":\"Animal sounds\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:7fda8ee0-3dcd-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:30.344Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: vega", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: vega\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n/*\\n\\nWelcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments.\\n\\nThis example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner.\\n*/\\n\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n title: Event counts from all indexes\\n\\n // Define the data source\\n data: {\\n url: {\\n/*\\nAn object instead of a string for the \\\"url\\\" param is treated as an Elasticsearch query. Anything inside this object is not part of the Vega language, but only understood by Kibana and Elasticsearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data.\\n\\nKibana has a special handling for the fields surrounded by \\\"%\\\". They are processed before the the query is sent to Elasticsearch. This way the query becomes context aware, and can use the time range and the dashboard filters.\\n*/\\n\\n // Apply dashboard context filters when set\\n %context%: true\\n // Filter the time picker (upper right corner) with this field\\n %timefield%: @timestamp\\n\\n/*\\nSee .search() documentation for : https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-search\\n*/\\n\\n // Which index to search\\n index: _all\\n // Aggregate data by the time field into time buckets, counting the number of documents in each bucket.\\n body: {\\n aggs: {\\n time_buckets: {\\n date_histogram: {\\n // Use date histogram aggregation on @timestamp field\\n field: @timestamp\\n // The interval value will depend on the daterange picker (true), or use an integer to set an approximate bucket count\\n interval: {%autointerval%: true}\\n // Make sure we get an entire range, even if it has no data\\n extended_bounds: {\\n // Use the current time range's start and end\\n min: {%timefilter%: \\\"min\\\"}\\n max: {%timefilter%: \\\"max\\\"}\\n }\\n // Use this for linear (e.g. line, area) graphs. Without it, empty buckets will not show up\\n min_doc_count: 0\\n }\\n }\\n }\\n // Speed up the response by only including aggregation results\\n size: 0\\n }\\n }\\n/*\\nElasticsearch will return results in this format:\\n\\naggregations: {\\n time_buckets: {\\n buckets: [\\n {\\n key_as_string: 2015-11-30T22:00:00.000Z\\n key: 1448920800000\\n doc_count: 0\\n },\\n {\\n key_as_string: 2015-11-30T23:00:00.000Z\\n key: 1448924400000\\n doc_count: 0\\n }\\n ...\\n ]\\n }\\n}\\n\\nFor our graph, we only need the list of bucket values. Use the format.property to discard everything else.\\n*/\\n format: {property: \\\"aggregations.time_buckets.buckets\\\"}\\n }\\n\\n // \\\"mark\\\" is the graphics element used to show our data. Other mark values are: area, bar, circle, line, point, rect, rule, square, text, and tick. See https://vega.github.io/vega-lite/docs/mark.html\\n mark: line\\n\\n // \\\"encoding\\\" tells the \\\"mark\\\" what data to use and in what way. See https://vega.github.io/vega-lite/docs/encoding.html\\n encoding: {\\n x: {\\n // The \\\"key\\\" value is the timestamp in milliseconds. Use it for X axis.\\n field: key\\n type: temporal\\n axis: {title: false} // Customize X axis format\\n }\\n y: {\\n // The \\\"doc_count\\\" is the count per bucket. Use it for Y axis.\\n field: doc_count\\n type: quantitative\\n axis: {title: \\\"Document count\\\"}\\n }\\n }\\n}\\n\"},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:02a2e4e0-3dcd-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:30.351Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: tsvb-table", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: tsvb-table\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"table\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"d18e5970-3dcc-11e8-a2f6-c162ca6cf6ea\",\"color\":\"rgba(160,70,216,1)\",\"split_mode\":\"filter\",\"metrics\":[{\"id\":\"d18e5971-3dcc-11e8-a2f6-c162ca6cf6ea\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"bar_color_rules\":[{\"id\":\"cd25a820-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_color_rules\":[{\"id\":\"e0be22e0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_width\":10,\"gauge_inner_width\":10,\"gauge_style\":\"half\",\"markdown\":\"\\nHi Avg last bytes: {{ average_of_bytes.last.raw }}\",\"pivot_id\":\"bytes\",\"pivot_label\":\"Hello\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:f81134a0-3dcc-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:30.355Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: tsvb-markdown", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: tsvb-markdown\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"markdown\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"d18e5970-3dcc-11e8-a2f6-c162ca6cf6ea\",\"color\":\"rgba(160,70,216,1)\",\"split_mode\":\"filter\",\"metrics\":[{\"id\":\"d18e5971-3dcc-11e8-a2f6-c162ca6cf6ea\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"bar_color_rules\":[{\"id\":\"cd25a820-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_color_rules\":[{\"id\":\"e0be22e0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_width\":10,\"gauge_inner_width\":10,\"gauge_style\":\"half\",\"markdown\":\"\\nHi Avg last bytes: {{ average_of_bytes.last.raw }}\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:df815d20-3dcc-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:30.349Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: tsvb-topn", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: tsvb-topn\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"top_n\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"d18e5970-3dcc-11e8-a2f6-c162ca6cf6ea\",\"color\":\"rgba(160,70,216,1)\",\"split_mode\":\"filter\",\"metrics\":[{\"id\":\"d18e5971-3dcc-11e8-a2f6-c162ca6cf6ea\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"bar_color_rules\":[{\"id\":\"cd25a820-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:cc43fab0-3dcc-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:30.353Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: tsvb-metric", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: tsvb-metric\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"metric\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"sum_of_squares\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:c40f4d40-3dcc-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:30.347Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Rendering Test: tsvb-ts", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: tsvb-ts\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"use_kibana_indexes\":false},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:ffa2e0c0-3dcb-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:33.153Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: goal", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}}}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: goal\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":true,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":4000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":2,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:33.162Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: datatable", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: datatable\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"clientip\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:3525b840-3dcb-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:33.163Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: bar", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: bar\",\"type\":\"horizontal_bar\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":3,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:e6140540-3dca-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:33.165Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.src\",\"value\":\"CN\",\"params\":{\"query\":\"CN\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.src\":{\"query\":\"CN\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: area with not filter", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: area with not filter\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"label\":\"\"},{\"input\":{\"query\":\"bytes:>10\",\"language\":\"lucene\"}}]}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:4c0c3f90-3e5a-11e8-9fc3-39e49624228e", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:33.166Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[{\"meta\":{\"field\":\"isDog\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"isDog\",\"value\":\"true\",\"params\":{\"value\":true},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"script\":{\"script\":{\"inline\":\"boolean compare(Supplier s, def v) {return s.get() == v;}compare(() -> { return doc['animal.keyword'].value == 'dog' }, params.value);\",\"lang\":\"painless\",\"params\":{\"value\":true}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"weightLbs:>40\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: scripted filter and query", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: scripted filter and query\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"sound.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:50643b60-3dd3-11e8-b2b9-5d5dc1715159", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:34.195Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Rendering Test: animal sounds pie", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: animal sounds pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"sound.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:771b4f10-3e59-11e8-9fc3-39e49624228e", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", - "name": "search_0", - "type": "search" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:34.200Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}" - }, - "savedSearchRefName": "search_0", - "title": "Rendering Test: animal weights linked to search", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Rendering Test: animal weights linked to search\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"name.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:76c7f020-4194-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:34.583Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Filter Bytes Test: tsvb top n with bytes filter", - "uiStateJSON": "{}", - "version": 1, - "visState":"{\"title\":\"Filter Bytes Test: tsvb top n with bytes filter\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"top_n\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"filters\",\"metrics\":[{\"id\":\"482d6560-4194-11e8-a461-7d278185cba4\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\",\"filter\":{\"query\":\"Filter Bytes Test:>1000\",\"language\":\"lucene\"},\"override_index_pattern\":0,\"series_index_pattern\":\"logstash-*\",\"series_time_field\":\"utc_time\",\"series_interval\":\"1m\",\"value_template\":\"\",\"split_filters\":[{\"filter\":{\"query\":\"Filter Bytes Test:>100\",\"language\":\"lucene\"},\"label\":\"\",\"color\":\"#68BC00\",\"id\":\"39a107e0-4194-11e8-a461-7d278185cba4\"}],\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"4fd5b150-4194-11e8-a461-7d278185cba4\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"4fd5b151-4194-11e8-a461-7d278185cba4\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"Filter Bytes Test:>3000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"06893260-4194-11e8-a461-7d278185cba4\"}],\"bar_color_rules\":[{\"id\":\"36a0e740-4194-11e8-a461-7d278185cba4\"}],\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:0ca8c600-4195-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "control_0_index_pattern", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:35.229Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Filter Bytes Test: input control with filter", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: input control with filter\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1523896850250\",\"fieldName\":\"bytes\",\"parent\":\"\",\"label\":\"Byte Options\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":10,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:039e4770-4194-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:35.220Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Filter Bytes Test: tsvb time series with bytes filter split by clientip", - "uiStateJSON": "{}", - "version": 1, - "visState":"{\"title\":\"Filter Bytes Test: tsvb time series with bytes filter split by clientip\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"terms\",\"metrics\":[{\"value\":\"\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"sum\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\",\"filter\":{\"query\":\"Filter Bytes Test:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"use_kibana_indexes\":false,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:760a9060-4190-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:35.235Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.src\",\"value\":\"US\",\"params\":{\"query\":\"US\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.src\":{\"query\":\"US\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Filter Bytes Test: max bytes in US - area chart with filter", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: max bytes in US - area chart with filter\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Max bytes\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Max bytes\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:b3e70d00-4190-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:35.236Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Filter Bytes Test: standard deviation heatmap with other bucket", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"-4,000 - 1,000\":\"rgb(247,252,245)\",\"1,000 - 6,000\":\"rgb(199,233,192)\",\"6,000 - 11,000\":\"rgb(116,196,118)\",\"11,000 - 16,000\":\"rgb(35,139,69)\"}}}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: standard deviation heatmap with other bucket\",\"type\":\"heatmap\",\"params\":{\"type\":\"heatmap\",\"addTooltip\":true,\"addLegend\":true,\"enableHover\":false,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Greens\",\"setColorRange\":false,\"colorsRange\":[],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"overwriteColor\":false,\"color\":\"#555\"}}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"std_dev\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"_term\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:c10c6b00-4191-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:36.267Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Filter Bytes Test: max bytes guage percent mode", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 1\":\"rgb(0,104,55)\",\"1 - 15\":\"rgb(255,255,190)\",\"15 - 100\":\"rgb(165,0,38)\"}}}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: max bytes guage percent mode\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"extendRange\":true,\"percentageMode\":true,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":500},{\"from\":500,\"to\":7500},{\"from\":7500,\"to\":50000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"Im subtext\",\"fontSize\":60,\"labelColor\":true},\"alignment\":\"horizontal\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:03d2afd0-4192-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:36.269Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Filter Bytes Test: Goal unique count", - "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10000\":\"rgb(0,104,55)\"}}}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: Goal unique count\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:7ff2c4c0-4191-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:36.270Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Filter Bytes Test: Data table top hit with significant terms geo.src", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: Data table top hit with significant terms geo.src\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"top_hits\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\",\"aggregate\":\"average\",\"size\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"geo.src\",\"size\":10}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:df72ad40-4194-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:06:36.276Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"bytes\",\"value\":\"0\",\"params\":{\"query\":0,\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"bytes\":{\"query\":0,\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Filter Bytes Test: tag cloud with not 0 bytes filter", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: tag cloud with not 0 bytes filter\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"bytes\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "search:be5accf0-3dca-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "search": "7.9.3" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "search": { - "columns": [ - "agent", - "bytes", - "clientip" - ], - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "sort": [ - [ - "@timestamp", - "desc" - ] - ], - "title": "Rendering Test: saved search", - "version": 1 - }, - "type": "search", - "updated_at": "2018-04-17T15:09:39.805Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "search:ca5ada40-3dca-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "search": "7.9.3" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "search": { - "columns": [ - "agent", - "bytes", - "clientip" - ], - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"meta\":{\"negate\":false,\"type\":\"phrase\",\"key\":\"bytes\",\"value\":\"1,607\",\"params\":{\"query\":1607,\"type\":\"phrase\"},\"disabled\":false,\"alias\":null,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"bytes\":{\"query\":1607,\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "sort": [ - [ - "@timestamp", - "desc" - ] - ], - "title": "Filter Bytes Test: search with filter", - "version": 1 - }, - "type": "search", - "updated_at": "2018-04-17T15:09:55.976Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:9bebe980-4192-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T15:59:42.648Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Filter Bytes Test: timelion split 5 on bytes", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: timelion split 5 on bytes\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(*, split=bytes:5)\",\"interval\":\"auto\"},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:1dcdfe30-4192-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T15:59:56.976Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"bytes:>100\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Filter Bytes Test: min bytes metric with query", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: min bytes metric with query\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"min\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:35417e50-4194-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T16:06:03.785Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Filter Bytes Test: tsvb metric with custom interval and bytes filter", - "uiStateJSON": "{}", - "version": 1, - "visState":"{\"title\":\"Filter Bytes Test: tsvb metric with custom interval and bytes filter\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"metric\",\"series\":[{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"value\":\"\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"sum\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\",\"filter\":{\"query\":\"Filter Bytes Test:>1000\",\"language\":\"lucene\"},\"override_index_pattern\":1,\"series_index_pattern\":\"logstash-*\",\"series_time_field\":\"utc_time\",\"series_interval\":\"1d\",\"value_template\":\"{{value}} custom template\",\"split_color_mode\":\"gradient\",\"series_drop_last_bucket\":1}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"06893260-4194-11e8-a461-7d278185cba4\"}],\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"isModelInvalid\":false,\"bar_color_rules\":[{\"id\":\"71f4e260-4186-11ec-8262-619fbabeae59\"}]}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:9fb4c670-4194-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T16:32:59.086Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Filter Bytes Test: tsvb markdown", - "uiStateJSON": "{}", - "version": 1, - "visState":"{\"title\":\"Filter Bytes Test: tsvb markdown\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"markdown\",\"series\":[{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"filters\",\"metrics\":[{\"id\":\"482d6560-4194-11e8-a461-7d278185cba4\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\",\"filter\":{\"query\":\"Filter Bytes Test:>1000\",\"language\":\"lucene\"},\"override_index_pattern\":0,\"series_index_pattern\":\"logstash-*\",\"series_time_field\":\"utc_time\",\"series_interval\":\"1m\",\"value_template\":\"\",\"split_filters\":[{\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"label\":\"\",\"color\":\"#68BC00\",\"id\":\"39a107e0-4194-11e8-a461-7d278185cba4\"}],\"label\":\"\",\"var_name\":\"\",\"split_color_mode\":\"gradient\",\"series_drop_last_bucket\":1}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"06893260-4194-11e8-a461-7d278185cba4\"}],\"bar_color_rules\":[{\"id\":\"36a0e740-4194-11e8-a461-7d278185cba4\"}],\"markdown\":\"{{bytes_1000.last.formatted}}\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:befdb6b0-3e59-11e8-9fc3-39e49624228e", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - }, - { - "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", - "name": "search_0", - "type": "search" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T17:16:27.743Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal.keyword\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal.keyword\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"language\":\"lucene\",\"query\":\"\"}}" - }, - "savedSearchRefName": "search_0", - "title": "Filter Test: animals: linked to search with filter", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Filter Test: animals: linked to search with filter\",\"type\":\"pie\",\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":true,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"right\",\"type\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"name.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:584c0300-4191-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-04-17T18:36:30.315Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"bytes:>9000\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Filter Bytes Test: split by geo with query", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: split by geo with query\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"sum\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "config:7.0.0-alpha1", - "index": ".kibana", - "source": { - "config": { - "buildNum": null, - "dateFormat:tz": "UTC", - "defaultIndex": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "notifications:lifetime:banner": 3600000, - "notifications:lifetime:error": 3600000, - "notifications:lifetime:info": 3600000, - "notifications:lifetime:warning": 3600000 - }, - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "config": "7.13.0" - }, - "references": [ - ], - "type": "config", - "updated_at": "2018-04-17T19:25:03.632Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:8090dcb0-4195-11e8-bb13-d53698fb349a", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - ], - "type": "visualization", - "updated_at": "2018-04-17T19:28:21.967Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{}" - }, - "title": "Filter Bytes Test: vega", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Filter Bytes Test: vega\",\"type\":\"vega\",\"params\":{\"spec\":\"{ \\nconfig: { kibana: { renderer: \\\"svg\\\" }},\\n/*\\n\\nWelcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments.\\n\\nThis example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner.\\n*/\\n\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n title: Event counts from all indexes\\n\\n // Define the data source\\n data: {\\n url: {\\n/*\\nAn object instead of a string for the \\\"url\\\" param is treated as an Elasticsearch query. Anything inside this object is not part of the Vega language, but only understood by Kibana and Elasticsearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data.\\n\\nKibana has a special handling for the fields surrounded by \\\"%\\\". They are processed before the the query is sent to Elasticsearch. This way the query becomes context aware, and can use the time range and the dashboard filters.\\n*/\\n\\n // Apply dashboard context filters when set\\n %context%: true\\n // Filter the time picker (upper right corner) with this field\\n %timefield%: @timestamp\\n\\n/*\\nSee .search() documentation for : https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-search\\n*/\\n\\n // Which index to search\\n index: _all\\n // Aggregate data by the time field into time buckets, counting the number of documents in each bucket.\\n body: {\\n aggs: {\\n time_buckets: {\\n date_histogram: {\\n // Use date histogram aggregation on @timestamp field\\n field: @timestamp\\n // The interval value will depend on the daterange picker (true), or use an integer to set an approximate bucket count\\n interval: {%autointerval%: true}\\n // Make sure we get an entire range, even if it has no data\\n extended_bounds: {\\n // Use the current time range's start and end\\n min: {%timefilter%: \\\"min\\\"}\\n max: {%timefilter%: \\\"max\\\"}\\n }\\n // Use this for linear (e.g. line, area) graphs. Without it, empty buckets will not show up\\n min_doc_count: 0\\n }\\n }\\n }\\n // Speed up the response by only including aggregation results\\n size: 0\\n }\\n }\\n/*\\nElasticsearch will return results in this format:\\n\\naggregations: {\\n time_buckets: {\\n buckets: [\\n {\\n key_as_string: 2015-11-30T22:00:00.000Z\\n key: 1448920800000\\n doc_count: 0\\n },\\n {\\n key_as_string: 2015-11-30T23:00:00.000Z\\n key: 1448924400000\\n doc_count: 0\\n }\\n ...\\n ]\\n }\\n}\\n\\nFor our graph, we only need the list of bucket values. Use the format.property to discard everything else.\\n*/\\n format: {property: \\\"aggregations.time_buckets.buckets\\\"}\\n }\\n\\n // \\\"mark\\\" is the graphics element used to show our data. Other mark values are: area, bar, circle, line, point, rect, rule, square, text, and tick. See https://vega.github.io/vega-lite/docs/mark.html\\n mark: line\\n\\n // \\\"encoding\\\" tells the \\\"mark\\\" what data to use and in what way. See https://vega.github.io/vega-lite/docs/encoding.html\\n encoding: {\\n x: {\\n // The \\\"key\\\" value is the timestamp in milliseconds. Use it for X axis.\\n field: key\\n type: temporal\\n axis: {title: false} // Customize X axis format\\n }\\n y: {\\n // The \\\"doc_count\\\" is the count per bucket. Use it for Y axis.\\n field: doc_count\\n type: quantitative\\n axis: {title: \\\"Document count\\\"}\\n }\\n }\\n}\\n\"},\"aggs\":[]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "config:6.2.4", - "index": ".kibana", - "source": { - "config": { - "buildNum": 16627, - "defaultIndex": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "xPackMonitoring:showBanner": false - }, - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "config": "7.13.0" - }, - "references": [ - ], - "type": "config", - "updated_at": "2018-05-09T20:50:57.021Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:edb65990-53ca-11e8-b481-c9426d020fcd", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-05-09T20:52:47.144Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "table created in 6_2", - "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", - "version": 1, - "visState": "{\"title\":\"table created in 6_2\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"weightLbs\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"animal.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:0644f890-53cb-11e8-b481-c9426d020fcd", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2018-05-09T20:53:28.345Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"weightLbs:>10\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "Weight in lbs pie created in 6.2", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"title\":\"Weight in lbs pie created in 6.2\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"weightLbs\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:1b2f47b0-53cb-11e8-b481-c9426d020fcd", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"weightLbs:>15\"},\"filter\":[{\"meta\":{\"field\":\"isDog\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"isDog\",\"value\":\"true\",\"params\":{\"value\":true},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"script\":{\"script\":{\"inline\":\"boolean compare(Supplier s, def v) {return s.get() == v;}compare(() -> { return doc['animal.keyword'].value == 'dog' }, params.value);\",\"lang\":\"painless\",\"params\":{\"value\":true}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}" - }, - "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":12,\"x\":24,\"y\":0,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":12,\"x\":0,\"y\":0,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"}]", - "refreshInterval": { - "display": "Off", - "pause": false, - "value": 0 - }, - "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", - "timeRestore": true, - "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", - "title": "Animal Weights (created in 6.2)", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - }, - { - "id": "edb65990-53ca-11e8-b481-c9426d020fcd", - "name": "4:panel_4", - "type": "visualization" - }, - { - "id": "0644f890-53cb-11e8-b481-c9426d020fcd", - "name": "5:panel_5", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2018-05-09T20:54:03.435Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "index-pattern:a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "index-pattern": { - "fieldFormatMap": "{\"weightLbs\":{\"id\":\"number\",\"params\":{\"pattern\":\"0,0.0\"}},\"is_dog\":{\"id\":\"boolean\"},\"isDog\":{\"id\":\"boolean\"}}", - "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"animal\",\"type\":\"string\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"animal.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"name\",\"type\":\"string\",\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sound\",\"type\":\"string\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"sound.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"weightLbs\",\"type\":\"number\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isDog\",\"type\":\"boolean\",\"count\":0,\"scripted\":true,\"script\":\"return doc['animal.keyword'].value == 'dog'\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", - "timeFieldName": "@timestamp", - "title": "animals-*" - }, - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [ - ], - "type": "index-pattern", - "updated_at": "2018-05-09T20:55:44.314Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "search:6351c590-53cb-11e8-b481-c9426d020fcd", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "search": "7.9.3" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - }, - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - } - ], - "search": { - "columns": [ - "animal", - "sound", - "weightLbs" - ], - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"weightLbs:>10\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"sound.keyword\",\"value\":\"growl\",\"params\":{\"query\":\"growl\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"sound.keyword\":{\"query\":\"growl\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "sort": [ - [ - "@timestamp", - "desc" - ] - ], - "title": "Search created in 6.2", - "version": 1 - }, - "type": "search", - "updated_at": "2018-05-09T20:56:04.457Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "visualization:47b5cf60-9e93-11ea-853e-adc0effaf76d", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "visualization": "7.14.0" - }, - "references": [ - { - "id": "1b1789d0-9e93-11ea-853e-adc0effaf76d", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern" - } - ], - "type": "visualization", - "updated_at": "2020-05-25T15:16:27.743Z", - "visualization": { - "description": "", - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" - }, - "title": "vis with missing index pattern", - "uiStateJSON": "{}", - "version": 1, - "visState": "{\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"vis with missing index pattern\"}" - } - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:502e63a0-9e93-11ea-853e-adc0effaf76d", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}" - }, - "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":true}", - "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"6cfbe6cc-1872-4cb4-9455-a02eeb75127e\"},\"panelIndex\":\"6cfbe6cc-1872-4cb4-9455-a02eeb75127e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6cfbe6cc-1872-4cb4-9455-a02eeb75127e\"}]", - "timeRestore": false, - "title": "dashboard with missing index pattern", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "47b5cf60-9e93-11ea-853e-adc0effaf76d", - "name": "6cfbe6cc-1872-4cb4-9455-a02eeb75127e:panel_6cfbe6cc-1872-4cb4-9455-a02eeb75127e", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2020-05-25T15:16:27.743Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "dashboard:6eb8a840-a32e-11ea-88c2-d56dd2b14bd7", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "dashboard": { - "description": "", - "hits": 0, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\n \"query\": {\n \"language\": \"kuery\",\n \"query\": \"\"\n },\n \"filter\": [\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": true,\n \"type\": \"phrase\",\n \"key\": \"name\",\n \"params\": {\n \"query\": \"moo\"\n },\n \"indexRefName\": \"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"\n },\n \"query\": {\n \"match_phrase\": {\n \"name\": \"moo\"\n }\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n },\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": true,\n \"type\": \"phrase\",\n \"key\": \"baad-field\",\n \"params\": {\n \"query\": \"moo\"\n },\n \"indexRefName\": \"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"\n },\n \"query\": {\n \"match_phrase\": {\n \"baad-field\": \"moo\"\n }\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n },\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": false,\n \"type\": \"phrase\",\n \"key\": \"@timestamp\",\n \"params\": {\n \"query\": \"123\"\n },\n \"indexRefName\": \"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index\"\n },\n \"query\": {\n \"match_phrase\": {\n \"@timestamp\": \"123\"\n }\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n },\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": false,\n \"type\": \"exists\",\n \"key\": \"extension\",\n \"value\": \"exists\",\n \"indexRefName\": \"kibanaSavedObjectMeta.searchSourceJSON.filter[3].meta.index\"\n },\n \"exists\": {\n \"field\": \"extension\"\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n },\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": false,\n \"type\": \"phrase\",\n \"key\": \"banana\",\n \"params\": {\n \"query\": \"yellow\"\n }\n },\n \"query\": {\n \"match_phrase\": {\n \"banana\": \"yellow\"\n }\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n }\n ]\n}" - }, - "optionsJSON": "{\n \"hidePanelTitles\": false,\n \"useMargins\": true\n}", - "panelsJSON": "[{\"version\":\"8.0.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"94a3dc1d-508a-4d42-a480-65b158925ba0\"},\"panelIndex\":\"94a3dc1d-508a-4d42-a480-65b158925ba0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_94a3dc1d-508a-4d42-a480-65b158925ba0\"}]", - "refreshInterval": { - "pause": true, - "value": 0 - }, - "timeFrom": "now-10y", - "timeRestore": true, - "timeTo": "now", - "title": "dashboard with bad filters", - "version": 1 - }, - "migrationVersion": { - "dashboard": "7.14.0" - }, - "references": [ - { - "id": "a0f483a0-3dc9-11e8-8660-bad-index", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern" - }, - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index", - "type": "index-pattern" - }, - { - "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[3].meta.index", - "type": "index-pattern" - }, - { - "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[4].meta.index", - "type": "index-pattern" - }, - { - "id": "50643b60-3dd3-11e8-b2b9-5d5dc1715159", - "name": "94a3dc1d-508a-4d42-a480-65b158925ba0:panel_94a3dc1d-508a-4d42-a480-65b158925ba0", - "type": "visualization" - } - ], - "type": "dashboard", - "updated_at": "2020-06-04T09:26:04.272Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "config:8.0.0", - "index": ".kibana", - "source": { - "config": { - "accessibility:disableAnimations": true, - "buildNum": null, - "dateFormat:tz": "UTC", - "defaultIndex": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c" - }, - "coreMigrationVersion": "7.14.0", - "migrationVersion": { - "config": "7.13.0" - }, - "references": [ - ], - "type": "config", - "updated_at": "2020-06-04T09:22:54.572Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "ui-metric:DashboardPanelVersionInUrl:8.0.0", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "references": [ - ], - "type": "ui-metric", - "ui-metric": { - "count": 15 - }, - "updated_at": "2020-06-04T09:28:06.848Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "ui-metric:kibana-user_agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "references": [ - ], - "type": "ui-metric", - "ui-metric": { - "count": 1 - }, - "updated_at": "2020-06-04T09:28:06.848Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "application_usage_daily:dashboards:2020-05-31", - "index": ".kibana", - "source": { - "application_usage_daily": { - "appId": "dashboards", - "minutesOnScreen": 13.956333333333333, - "numberOfClicks": 134, - "timestamp": "2020-05-31T00:00:00.000Z" - }, - "coreMigrationVersion": "7.14.0", - "references": [ - ], - "type": "application_usage_daily", - "updated_at": "2021-06-10T22:39:09.215Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "application_usage_daily:home:2020-05-31", - "index": ".kibana", - "source": { - "application_usage_daily": { - "appId": "home", - "minutesOnScreen": 0.5708666666666666, - "numberOfClicks": 1, - "timestamp": "2020-05-31T00:00:00.000Z" - }, - "coreMigrationVersion": "7.14.0", - "references": [ - ], - "type": "application_usage_daily", - "updated_at": "2021-06-10T22:39:09.215Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "application_usage_daily:management:2020-05-31", - "index": ".kibana", - "source": { - "application_usage_daily": { - "appId": "management", - "minutesOnScreen": 5.842616666666666, - "numberOfClicks": 107, - "timestamp": "2020-05-31T00:00:00.000Z" - }, - "coreMigrationVersion": "7.14.0", - "references": [ - ], - "type": "application_usage_daily", - "updated_at": "2021-06-10T22:39:09.215Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "application_usage_daily:management:2020-06-04", - "index": ".kibana", - "source": { - "application_usage_daily": { - "appId": "management", - "minutesOnScreen": 2.5120666666666667, - "numberOfClicks": 38, - "timestamp": "2020-06-04T00:00:00.000Z" - }, - "coreMigrationVersion": "7.14.0", - "references": [ - ], - "type": "application_usage_daily", - "updated_at": "2021-06-10T22:39:09.215Z" - }, - "type": "_doc" - } -} - -{ - "type": "doc", - "value": { - "id": "application_usage_daily:dashboards:2020-06-04", - "index": ".kibana", - "source": { - "application_usage_daily": { - "appId": "dashboards", - "minutesOnScreen": 9.065083333333334, - "numberOfClicks": 21, - "timestamp": "2020-06-04T00:00:00.000Z" - }, - "coreMigrationVersion": "7.14.0", - "references": [ - ], - "type": "application_usage_daily", - "updated_at": "2021-06-10T22:39:09.215Z" - }, - "type": "_doc" - } -} diff --git a/test/functional/fixtures/es_archiver/dashboard/current/kibana/mappings.json b/test/functional/fixtures/es_archiver/dashboard/current/kibana/mappings.json deleted file mode 100644 index 161d733e868a8..0000000000000 --- a/test/functional/fixtures/es_archiver/dashboard/current/kibana/mappings.json +++ /dev/null @@ -1,476 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana_$KIBANA_PACKAGE_VERSION": {}, - ".kibana": {} - }, - "index": ".kibana_$KIBANA_PACKAGE_VERSION_001", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", - "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", - "application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724", - "config": "c63748b75f39d0c54de12d12c1ccbc20", - "core-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", - "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", - "dashboard": "40554caf09725935e2c02e02563a2d07", - "index-pattern": "45915a1ad866812242df474eb0479052", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "legacy-url-alias": "6155300fd11a00e23d5cbaa39f0fce0a", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "namespace": "2f4316de49999235636386fe51dc06c1", - "namespaces": "2f4316de49999235636386fe51dc06c1", - "originId": "2f4316de49999235636386fe51dc06c1", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "db2c00e39b36f40930a3b9fc71c823e1", - "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", - "telemetry": "36a616f7026dfa617d6655df850fe16d", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "usage-counters": "8cc260bdceffec4ffc3ad165c97dc1b4", - "visualization": "f819cf6636b75c9e76ba733a0c6ef355" - } - }, - "dynamic": "strict", - "properties": { - "application_usage_daily": { - "dynamic": "false", - "properties": { - "timestamp": { - "type": "date" - } - } - }, - "application_usage_totals": { - "dynamic": "false", - "type": "object" - }, - "application_usage_transactional": { - "dynamic": "false", - "type": "object" - }, - "config": { - "dynamic": "false", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "core-usage-stats": { - "dynamic": "false", - "type": "object" - }, - "coreMigrationVersion": { - "type": "keyword" - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "optionsJSON": { - "index": false, - "type": "text" - }, - "panelsJSON": { - "index": false, - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "pause": { - "doc_values": false, - "index": false, - "type": "boolean" - }, - "section": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "value": { - "doc_values": false, - "index": false, - "type": "integer" - } - } - }, - "timeFrom": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "timeRestore": { - "doc_values": false, - "index": false, - "type": "boolean" - }, - "timeTo": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "index-pattern": { - "dynamic": "false", - "properties": { - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "legacy-url-alias": { - "dynamic": "false", - "properties": { - "disabled": { - "type": "boolean" - }, - "sourceId": { - "type": "keyword" - }, - "targetType": { - "type": "keyword" - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "config": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "dashboard": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "index-pattern": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "search": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "visualization": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "namespace": { - "type": "keyword" - }, - "namespaces": { - "type": "keyword" - }, - "originId": { - "type": "keyword" - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "description": { - "type": "text" - }, - "grid": { - "enabled": false, - "type": "object" - }, - "hideChart": { - "doc_values": false, - "index": false, - "type": "boolean" - }, - "hits": { - "doc_values": false, - "index": false, - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "sort": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "search-telemetry": { - "dynamic": "false", - "type": "object" - }, - "server": { - "dynamic": "false", - "type": "object" - }, - "telemetry": { - "properties": { - "allowChangingOptInStatus": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "type": "keyword" - }, - "reportFailureCount": { - "type": "integer" - }, - "reportFailureVersion": { - "type": "keyword" - }, - "sendUsageFrom": { - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "tsvb-validation-telemetry": { - "dynamic": "false", - "type": "object" - }, - "type": { - "type": "keyword" - }, - "ui-counter": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "usage-counters": { - "dynamic": "false", - "properties": { - "domainId": { - "type": "keyword" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "index": false, - "type": "text" - } - } - }, - "savedSearchRefName": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "index": false, - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "index": false, - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "10", - "refresh_interval": "1s", - "routing_partition_size": "1" - } - } - } -} \ No newline at end of file diff --git a/test/functional/fixtures/kbn_archiver/dashboard/current/kibana.json b/test/functional/fixtures/kbn_archiver/dashboard/current/kibana.json new file mode 100644 index 0000000000000..711f242d16ac8 --- /dev/null +++ b/test/functional/fixtures/kbn_archiver/dashboard/current/kibana.json @@ -0,0 +1,2653 @@ +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: tsvb-table", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: tsvb-table\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"table\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"d18e5970-3dcc-11e8-a2f6-c162ca6cf6ea\",\"color\":\"rgba(160,70,216,1)\",\"split_mode\":\"filter\",\"metrics\":[{\"id\":\"d18e5971-3dcc-11e8-a2f6-c162ca6cf6ea\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"bar_color_rules\":[{\"id\":\"cd25a820-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_color_rules\":[{\"id\":\"e0be22e0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_width\":10,\"gauge_inner_width\":10,\"gauge_style\":\"half\",\"markdown\":\"\\nHi Avg last bytes: {{ average_of_bytes.last.raw }}\",\"pivot_id\":\"bytes\",\"pivot_label\":\"Hello\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "02a2e4e0-3dcd-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:30.351Z", + "version": "WzIzMSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Filter Bytes Test: tsvb time series with bytes filter split by clientip", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: tsvb time series with bytes filter split by clientip\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"terms\",\"metrics\":[{\"value\":\"\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"sum\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\",\"filter\":{\"query\":\"Filter Bytes Test:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"use_kibana_indexes\":false,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "039e4770-4194-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:35.220Z", + "version": "WzI0NSwxXQ==" +} + +{ + "attributes": { + "fieldFormatMap": "{\"machine.ram\":{\"id\":\"number\",\"params\":{\"pattern\":\"0,0.[000] b\"}}}", + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "logstash-*" + }, + "coreMigrationVersion": "8.0.1", + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-04-16T16:57:12.263Z", + "version": "WzIxNiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Filter Bytes Test: Goal unique count", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 10000\":\"rgb(0,104,55)\"}}}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: Goal unique count\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":false,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "03d2afd0-4192-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:36.269Z", + "version": "WzI0OSwxXQ==" +} + +{ + "attributes": { + "fieldFormatMap": "{\"weightLbs\":{\"id\":\"number\",\"params\":{\"pattern\":\"0,0.0\"}},\"is_dog\":{\"id\":\"boolean\"},\"isDog\":{\"id\":\"boolean\"}}", + "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"animal\",\"type\":\"string\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"animal.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"name\",\"type\":\"string\",\"count\":1,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sound\",\"type\":\"string\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"sound.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"weightLbs\",\"type\":\"number\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isDog\",\"type\":\"boolean\",\"count\":0,\"scripted\":true,\"script\":\"return doc['animal.keyword'].value == 'dog'\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", + "timeFieldName": "@timestamp", + "title": "animals-*" + }, + "coreMigrationVersion": "8.0.1", + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-05-09T20:55:44.314Z", + "version": "WzI2NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"weightLbs:>10\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Weight in lbs pie created in 6.2", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Weight in lbs pie created in 6.2\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"weightLbs\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "0644f890-53cb-11e8-b481-c9426d020fcd", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-05-09T20:53:28.345Z", + "version": "WzI2NCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Filter Bytes Test: input control with filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: input control with filter\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1523896850250\",\"fieldName\":\"bytes\",\"parent\":\"\",\"label\":\"Byte Options\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":10,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "0ca8c600-4195-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "control_0_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:35.229Z", + "version": "WzI0NCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: metric", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: metric\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"cardinality\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "11ae2bd0-3dcc-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:32.133Z", + "version": "WzIyMSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: heatmap", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 15\":\"rgb(247,252,245)\",\"15 - 30\":\"rgb(199,233,192)\",\"30 - 45\":\"rgb(116,196,118)\",\"45 - 60\":\"rgb(35,139,69)\"}}}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: heatmap\",\"type\":\"heatmap\",\"params\":{\"type\":\"heatmap\",\"addTooltip\":true,\"addLegend\":true,\"enableHover\":false,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Greens\",\"setColorRange\":false,\"colorsRange\":[],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"overwriteColor\":false,\"color\":\"#555\"}}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"bytes\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "145ced90-3dcb-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:32.134Z", + "version": "WzIyMiwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "im empty", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "14616b50-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:02:51.909Z", + "version": "WzE5NCwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "im empty too", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "19523860-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:03:00.198Z", + "version": "WzE5MywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "table created in 6_2", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"title\":\"table created in 6_2\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"weightLbs\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"animal.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "edb65990-53ca-11e8-b481-c9426d020fcd", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-05-09T20:52:47.144Z", + "version": "WzI2MywxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"weightLbs:>15\"},\"filter\":[{\"meta\":{\"field\":\"isDog\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"isDog\",\"value\":\"true\",\"params\":{\"value\":true},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"script\":{\"script\":{\"inline\":\"boolean compare(Supplier s, def v) {return s.get() == v;}compare(() -> { return doc['animal.keyword'].value == 'dog' }, params.value);\",\"lang\":\"painless\",\"params\":{\"value\":true}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":12,\"x\":24,\"y\":0,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":12,\"x\":0,\"y\":0,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"}]", + "refreshInterval": { + "display": "Off", + "pause": false, + "value": 0 + }, + "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", + "timeRestore": true, + "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", + "title": "Animal Weights (created in 6.2)", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "1b2f47b0-53cb-11e8-b481-c9426d020fcd", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "edb65990-53ca-11e8-b481-c9426d020fcd", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "0644f890-53cb-11e8-b481-c9426d020fcd", + "name": "5:panel_5", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-05-09T20:54:03.435Z", + "version": "WzI2NSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"bytes:>100\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Filter Bytes Test: min bytes metric with query", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: min bytes metric with query\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"min\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "1dcdfe30-4192-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:59:56.976Z", + "version": "WzI1NSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.src\",\"value\":\"CN\",\"params\":{\"query\":\"CN\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.src\":{\"query\":\"CN\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"bytes >= 10000\",\"language\":\"kuery\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Kuery: pie bytes with kuery and filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Kuery: pie bytes with kuery and filter\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"bytes\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "29bd0240-4197-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-16T16:56:53.092Z", + "version": "WzIxNSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: guage", + "uiStateJSON": "{\"vis\":{\"colors\":{\"0 - 50000\":\"#EF843C\",\"75000 - 10000000\":\"#3F6833\"},\"defaultColors\":{\"0 - 5000000\":\"rgb(0,104,55)\",\"50000000 - 74998990099\":\"rgb(165,0,38)\"}}}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: guage\",\"type\":\"gauge\",\"params\":{\"addLegend\":true,\"addTooltip\":true,\"gauge\":{\"backStyle\":\"Full\",\"colorSchema\":\"Green to Red\",\"colorsRange\":[{\"from\":0,\"to\":5000000},{\"from\":50000000,\"to\":74998990099}],\"extendRange\":true,\"gaugeColorMode\":\"Labels\",\"gaugeStyle\":\"Full\",\"gaugeType\":\"Arc\",\"invertColors\":false,\"labels\":{\"color\":\"black\",\"show\":true},\"orientation\":\"vertical\",\"percentageMode\":false,\"scale\":{\"color\":\"#333\",\"labels\":false,\"show\":true},\"style\":{\"bgColor\":false,\"bgFill\":\"#eee\",\"bgMask\":false,\"bgWidth\":0.9,\"fontSize\":60,\"labelColor\":true,\"mask\":false,\"maskBars\":50,\"subText\":\"\",\"width\":0.9},\"type\":\"meter\",\"alignment\":\"horizontal\"},\"isDisplayWarning\":false,\"type\":\"gauge\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"machine.ram\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "e2023110-3dcb-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:32.135Z", + "version": "WzIyMywxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":0,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"}]", + "timeRestore": false, + "title": "couple panels", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "2ae34a60-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "145ced90-3dcb-11e8-8660-4d65aa086b3c", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "e2023110-3dcb-11e8-8660-4d65aa086b3c", + "name": "2:panel_2", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-04-11T22:03:29.670Z", + "version": "WzE4OSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: input control", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: input control\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1523481142694\",\"fieldName\":\"bytes\",\"parent\":\"\",\"label\":\"Bytes Input List\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1523481163654\",\"fieldName\":\"bytes\",\"parent\":\"\",\"label\":\"Bytes range\",\"type\":\"range\",\"options\":{\"decimalPlaces\":0,\"step\":1},\"indexPatternRefName\":\"control_1_index_pattern\"},{\"id\":\"1523481176519\",\"fieldName\":\"sound.keyword\",\"parent\":\"\",\"label\":\"Animal sounds\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_2_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "2d1b1620-3dcd-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "control_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "control_1_index_pattern", + "type": "index-pattern" + }, + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "control_2_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:31.123Z", + "version": "WzIyNywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: datatable", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: datatable\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"clientip\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:33.162Z", + "version": "WzIzNywxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"}]", + "timeRestore": false, + "title": "few panels", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "33bb8ad0-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "145ced90-3dcb-11e8-8660-4d65aa086b3c", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "e2023110-3dcb-11e8-8660-4d65aa086b3c", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", + "name": "3:panel_3", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-04-11T22:03:44.509Z", + "version": "WzE5NSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: bar", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: bar\",\"type\":\"horizontal_bar\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":3,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "3525b840-3dcb-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:33.163Z", + "version": "WzIzOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Filter Bytes Test: tsvb metric with custom interval and bytes filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: tsvb metric with custom interval and bytes filter\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"metric\",\"series\":[{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"value\":\"\",\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"sum\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\",\"filter\":{\"query\":\"Filter Bytes Test:>1000\",\"language\":\"lucene\"},\"override_index_pattern\":1,\"series_index_pattern\":\"logstash-*\",\"series_time_field\":\"utc_time\",\"series_interval\":\"1d\",\"value_template\":\"{{value}} custom template\",\"split_color_mode\":\"gradient\",\"series_drop_last_bucket\":1}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"06893260-4194-11e8-a461-7d278185cba4\"}],\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":0,\"isModelInvalid\":false,\"bar_color_rules\":[{\"id\":\"71f4e260-4186-11ec-8262-619fbabeae59\"}]}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "35417e50-4194-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T16:06:03.785Z", + "version": "WzI1NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "1", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "3de0bda0-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:04:01.530Z", + "version": "WzIwMiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: pie", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"bytes\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "3fe22200-3dcb-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:32.130Z", + "version": "WzIyMCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: input control parent", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: input control parent\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1523481216736\",\"fieldName\":\"animal.keyword\",\"parent\":\"\",\"label\":\"Animal type\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1523481176519\",\"fieldName\":\"sound.keyword\",\"parent\":\"1523481216736\",\"label\":\"Animal sounds\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_1_index_pattern\"}],\"updateFiltersOnChange\":false,\"useTimeFilter\":false,\"pinFilters\":false},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "42535e30-3dcd-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "control_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "control_1_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:31.124Z", + "version": "WzIyOSwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "2", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "46c8b580-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:04:16.472Z", + "version": "WzIwMywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "vis with missing index pattern", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"title\":\"vis with missing index pattern\"}" + }, + "coreMigrationVersion": "8.0.1", + "id": "47b5cf60-9e93-11ea-853e-adc0effaf76d", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "1b1789d0-9e93-11ea-853e-adc0effaf76d", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-05-25T15:16:27.743Z", + "version": "WzI2OCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"meta\":{\"field\":\"isDog\",\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"isDog\",\"value\":\"true\",\"params\":{\"value\":true},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"script\":{\"script\":{\"inline\":\"boolean compare(Supplier s, def v) {return s.get() == v;}compare(() -> { return doc['animal.keyword'].value == 'dog' }, params.value);\",\"lang\":\"painless\",\"params\":{\"value\":true}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"weightLbs:>40\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: scripted filter and query", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: scripted filter and query\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"sound.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "4c0c3f90-3e5a-11e8-9fc3-39e49624228e", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:33.166Z", + "version": "WzI0MCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: markdown", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: markdown\",\"type\":\"markdown\",\"params\":{\"fontSize\":20,\"openLinksInNewTab\":false,\"markdown\":\"I'm a markdown!\"},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "4c0f47e0-3dcd-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:31.111Z", + "version": "WzIyNiwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "zz", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "4f0fd980-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:04:30.360Z", + "version": "WzIwMSwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}" + }, + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"6cfbe6cc-1872-4cb4-9455-a02eeb75127e\"},\"panelIndex\":\"6cfbe6cc-1872-4cb4-9455-a02eeb75127e\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6cfbe6cc-1872-4cb4-9455-a02eeb75127e\"}]", + "timeRestore": false, + "title": "dashboard with missing index pattern", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "502e63a0-9e93-11ea-853e-adc0effaf76d", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "47b5cf60-9e93-11ea-853e-adc0effaf76d", + "name": "6cfbe6cc-1872-4cb4-9455-a02eeb75127e:panel_6cfbe6cc-1872-4cb4-9455-a02eeb75127e", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-05-25T15:16:27.743Z", + "version": "WzI2OSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: animal sounds pie", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: animal sounds pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"sound.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "50643b60-3dd3-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:34.195Z", + "version": "WzI0MSwxXQ==" +} + +{ + "attributes": { + "columns": [ + "agent", + "bytes", + "clientip" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"clientip : 73.14.212.83\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"range\",\"key\":\"bytes\",\"value\":\"100 to 1,000\",\"params\":{\"gte\":100,\"lt\":1000},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"range\":{\"bytes\":{\"gte\":100,\"lt\":1000}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Bytes and kuery in saved search with filter", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "55d37a30-4197-11e8-bb13-d53698fb349a", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2018-04-16T16:58:07.059Z", + "version": "WzIxNywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"bytes:>9000\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Filter Bytes Test: split by geo with query", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: split by geo with query\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"sum\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "584c0300-4191-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T18:36:30.315Z", + "version": "WzI1OSwxXQ==" +} + +{ + "attributes": { + "columns": [ + "animal", + "isDog", + "name", + "sound", + "weightLbs" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"weightLbs:>40\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "sort": [ + [ + "weightLbs", + "desc" + ] + ], + "title": "animal weights", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2018-04-11T20:55:26.317Z", + "version": "WzE4NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Rendering Test: animal weights linked to search", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: animal weights linked to search\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"name.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "771b4f10-3e59-11e8-9fc3-39e49624228e", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:34.200Z", + "version": "WzI0MiwxXQ==" +} + +{ + "attributes": { + "description": "dashboard with scripted filter, negated filter and query", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"weightLbs:<50\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"name.keyword\",\"negate\":true,\"params\":{\"query\":\"Fee Fee\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"Fee Fee\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"name.keyword\":{\"query\":\"Fee Fee\",\"type\":\"phrase\"}}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":\"is dog\",\"disabled\":false,\"field\":\"isDog\",\"key\":\"isDog\",\"negate\":false,\"params\":{\"value\":true},\"type\":\"phrase\",\"value\":\"true\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"script\":{\"script\":{\"inline\":\"boolean compare(Supplier s, def v) {return s.get() == v;}compare(() -> { return doc['animal.keyword'].value == 'dog' }, params.value);\",\"lang\":\"painless\",\"params\":{\"value\":true}}}}],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":true,\"hidePanelTitles\":false,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"}]", + "refreshInterval": { + "display": "Off", + "pause": false, + "section": 0, + "value": 0 + }, + "timeFrom": "Wed Apr 12 2017 10:06:21 GMT-0400", + "timeRestore": true, + "timeTo": "Thu Apr 12 2018 10:06:21 GMT-0400", + "title": "filters", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "5bac3a80-3e5b-11e8-9fc3-39e49624228e", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", + "type": "index-pattern" + }, + { + "id": "771b4f10-3e59-11e8-9fc3-39e49624228e", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "4c0c3f90-3e5a-11e8-9fc3-39e49624228e", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "50643b60-3dd3-11e8-b2b9-5d5dc1715159", + "name": "4:panel_4", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-04-12T14:11:13.576Z", + "version": "WzIwOSwxXQ==" +} + +{ + "attributes": { + "fields": "[{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"activity level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"barking level\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"breed\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"breed.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"size\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"size.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"trainability\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "title": "dogbreeds" + }, + "coreMigrationVersion": "8.0.1", + "id": "f908c8e0-3e6d-11e8-bbb9-e15942d5d48c", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-04-12T16:24:29.357Z", + "version": "WzIxMCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "non timebased line chart - dog data", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"non timebased line chart - dog data\",\"type\":\"line\",\"params\":{\"type\":\"line\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Max trainability\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"line\",\"mode\":\"normal\",\"data\":{\"label\":\"Max trainability\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true},{\"show\":true,\"mode\":\"normal\",\"type\":\"line\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"3\",\"label\":\"Max barking level\"},\"valueAxis\":\"ValueAxis-1\"},{\"show\":true,\"mode\":\"normal\",\"type\":\"line\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"4\",\"label\":\"Max activity level\"},\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"trainability\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"breed.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"barking level\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"activity level\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "5e085850-3e6e-11e8-bbb9-e15942d5d48c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "f908c8e0-3e6d-11e8-bbb9-e15942d5d48c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-12T16:27:17.973Z", + "version": "WzIxMSwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "zz 2", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "60659030-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:04:59.443Z", + "version": "WzE5NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":0,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"}]", + "refreshInterval": { + "display": "Off", + "pause": false, + "value": 0 + }, + "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", + "timeRestore": true, + "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", + "title": "dashboard with filter", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "61c58ad0-3dd3-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "50643b60-3dd3-11e8-b2b9-5d5dc1715159", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", + "name": "2:panel_2", + "type": "search" + } + ], + "type": "dashboard", + "updated_at": "2018-04-11T21:57:52.253Z", + "version": "WzE4OCwxXQ==" +} + +{ + "attributes": { + "columns": [ + "animal", + "sound", + "weightLbs" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"weightLbs:>10\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"sound.keyword\",\"value\":\"growl\",\"params\":{\"query\":\"growl\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"sound.keyword\":{\"query\":\"growl\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Search created in 6.2", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "6351c590-53cb-11e8-b481-c9426d020fcd", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2018-05-09T20:56:04.457Z", + "version": "WzI2NywxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "zz 3", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "65227c00-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:05:07.392Z", + "version": "WzE5NywxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "zz 4", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "6803a2f0-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:05:12.223Z", + "version": "WzE5OCwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "zz 5", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "6b18f940-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:05:17.396Z", + "version": "WzE5OSwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "dashboard-name-has-dashes", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "6c0b16e0-3dd3-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T21:58:09.486Z", + "version": "WzE5MiwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "zz 6", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "6e12ff60-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:05:22.390Z", + "version": "WzIwMCwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\n \"query\": {\n \"language\": \"kuery\",\n \"query\": \"\"\n },\n \"filter\": [\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": true,\n \"type\": \"phrase\",\n \"key\": \"name\",\n \"params\": {\n \"query\": \"moo\"\n },\n \"indexRefName\": \"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"\n },\n \"query\": {\n \"match_phrase\": {\n \"name\": \"moo\"\n }\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n },\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": true,\n \"type\": \"phrase\",\n \"key\": \"baad-field\",\n \"params\": {\n \"query\": \"moo\"\n },\n \"indexRefName\": \"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"\n },\n \"query\": {\n \"match_phrase\": {\n \"baad-field\": \"moo\"\n }\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n },\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": false,\n \"type\": \"phrase\",\n \"key\": \"@timestamp\",\n \"params\": {\n \"query\": \"123\"\n },\n \"indexRefName\": \"kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index\"\n },\n \"query\": {\n \"match_phrase\": {\n \"@timestamp\": \"123\"\n }\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n },\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": false,\n \"type\": \"exists\",\n \"key\": \"extension\",\n \"value\": \"exists\",\n \"indexRefName\": \"kibanaSavedObjectMeta.searchSourceJSON.filter[3].meta.index\"\n },\n \"exists\": {\n \"field\": \"extension\"\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n },\n {\n \"meta\": {\n \"alias\": null,\n \"negate\": false,\n \"disabled\": false,\n \"type\": \"phrase\",\n \"key\": \"banana\",\n \"params\": {\n \"query\": \"yellow\"\n }\n },\n \"query\": {\n \"match_phrase\": {\n \"banana\": \"yellow\"\n }\n },\n \"$state\": {\n \"store\": \"appState\"\n }\n }\n ]\n}" + }, + "optionsJSON": "{\n \"hidePanelTitles\": false,\n \"useMargins\": true\n}", + "panelsJSON": "[{\"version\":\"8.0.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"94a3dc1d-508a-4d42-a480-65b158925ba0\"},\"panelIndex\":\"94a3dc1d-508a-4d42-a480-65b158925ba0\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_94a3dc1d-508a-4d42-a480-65b158925ba0\"}]", + "refreshInterval": { + "pause": true, + "value": 0 + }, + "timeFrom": "now-10y", + "timeRestore": true, + "timeTo": "now", + "title": "dashboard with bad filters", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "6eb8a840-a32e-11ea-88c2-d56dd2b14bd7", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-bad-index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[3].meta.index", + "type": "index-pattern" + }, + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[4].meta.index", + "type": "index-pattern" + }, + { + "id": "50643b60-3dd3-11e8-b2b9-5d5dc1715159", + "name": "94a3dc1d-508a-4d42-a480-65b158925ba0:panel_94a3dc1d-508a-4d42-a480-65b158925ba0", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-06-04T09:26:04.272Z", + "version": "WzI3MCwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "zz 7", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "708fe640-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:05:26.564Z", + "version": "WzIwNCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.src\",\"value\":\"US\",\"params\":{\"query\":\"US\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.src\":{\"query\":\"US\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Filter Bytes Test: max bytes in US - area chart with filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: max bytes in US - area chart with filter\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Max bytes\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Max bytes\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "760a9060-4190-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:35.235Z", + "version": "WzI0NiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Filter Bytes Test: tsvb top n with bytes filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: tsvb top n with bytes filter\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"top_n\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"filters\",\"metrics\":[{\"id\":\"482d6560-4194-11e8-a461-7d278185cba4\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\",\"filter\":{\"query\":\"Filter Bytes Test:>1000\",\"language\":\"lucene\"},\"override_index_pattern\":0,\"series_index_pattern\":\"logstash-*\",\"series_time_field\":\"utc_time\",\"series_interval\":\"1m\",\"value_template\":\"\",\"split_filters\":[{\"filter\":{\"query\":\"Filter Bytes Test:>100\",\"language\":\"lucene\"},\"label\":\"\",\"color\":\"#68BC00\",\"id\":\"39a107e0-4194-11e8-a461-7d278185cba4\"}],\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"4fd5b150-4194-11e8-a461-7d278185cba4\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"4fd5b151-4194-11e8-a461-7d278185cba4\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"Filter Bytes Test:>3000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"06893260-4194-11e8-a461-7d278185cba4\"}],\"bar_color_rules\":[{\"id\":\"36a0e740-4194-11e8-a461-7d278185cba4\"}],\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "76c7f020-4194-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:34.583Z", + "version": "WzI0MywxXQ==" +} + +{ + "attributes": { + "description": "and_descriptions_has_underscores", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "dashboard_with_underscores", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "76d03330-3dd3-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T21:58:27.555Z", + "version": "WzE5MCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: tag cloud", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: tag cloud\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "78803be0-3dcd-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:32.127Z", + "version": "WzIxOSwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "Hi i have a lot of words in my dashboard name! It's pretty long i wonder what it'll look like", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "7b8d50a0-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:05:45.002Z", + "version": "WzIwNSwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "bye", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "7e42d3b0-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:05:49.547Z", + "version": "WzIwNiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: vega", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: vega\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n/*\\n\\nWelcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments.\\n\\nThis example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner.\\n*/\\n\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n title: Event counts from all indexes\\n\\n // Define the data source\\n data: {\\n url: {\\n/*\\nAn object instead of a string for the \\\"url\\\" param is treated as an Elasticsearch query. Anything inside this object is not part of the Vega language, but only understood by Kibana and Elasticsearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data.\\n\\nKibana has a special handling for the fields surrounded by \\\"%\\\". They are processed before the the query is sent to Elasticsearch. This way the query becomes context aware, and can use the time range and the dashboard filters.\\n*/\\n\\n // Apply dashboard context filters when set\\n %context%: true\\n // Filter the time picker (upper right corner) with this field\\n %timefield%: @timestamp\\n\\n/*\\nSee .search() documentation for : https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-search\\n*/\\n\\n // Which index to search\\n index: _all\\n // Aggregate data by the time field into time buckets, counting the number of documents in each bucket.\\n body: {\\n aggs: {\\n time_buckets: {\\n date_histogram: {\\n // Use date histogram aggregation on @timestamp field\\n field: @timestamp\\n // The interval value will depend on the daterange picker (true), or use an integer to set an approximate bucket count\\n interval: {%autointerval%: true}\\n // Make sure we get an entire range, even if it has no data\\n extended_bounds: {\\n // Use the current time range's start and end\\n min: {%timefilter%: \\\"min\\\"}\\n max: {%timefilter%: \\\"max\\\"}\\n }\\n // Use this for linear (e.g. line, area) graphs. Without it, empty buckets will not show up\\n min_doc_count: 0\\n }\\n }\\n }\\n // Speed up the response by only including aggregation results\\n size: 0\\n }\\n }\\n/*\\nElasticsearch will return results in this format:\\n\\naggregations: {\\n time_buckets: {\\n buckets: [\\n {\\n key_as_string: 2015-11-30T22:00:00.000Z\\n key: 1448920800000\\n doc_count: 0\\n },\\n {\\n key_as_string: 2015-11-30T23:00:00.000Z\\n key: 1448924400000\\n doc_count: 0\\n }\\n ...\\n ]\\n }\\n}\\n\\nFor our graph, we only need the list of bucket values. Use the format.property to discard everything else.\\n*/\\n format: {property: \\\"aggregations.time_buckets.buckets\\\"}\\n }\\n\\n // \\\"mark\\\" is the graphics element used to show our data. Other mark values are: area, bar, circle, line, point, rect, rule, square, text, and tick. See https://vega.github.io/vega-lite/docs/mark.html\\n mark: line\\n\\n // \\\"encoding\\\" tells the \\\"mark\\\" what data to use and in what way. See https://vega.github.io/vega-lite/docs/encoding.html\\n encoding: {\\n x: {\\n // The \\\"key\\\" value is the timestamp in milliseconds. Use it for X axis.\\n field: key\\n type: temporal\\n axis: {title: false} // Customize X axis format\\n }\\n y: {\\n // The \\\"doc_count\\\" is the count per bucket. Use it for Y axis.\\n field: doc_count\\n type: quantitative\\n axis: {title: \\\"Document count\\\"}\\n }\\n }\\n}\\n\"},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "7fda8ee0-3dcd-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:30.344Z", + "version": "WzIzMCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Filter Bytes Test: Data table top hit with significant terms geo.src", + "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: Data table top hit with significant terms geo.src\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"top_hits\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\",\"aggregate\":\"average\",\"size\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"significant_terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"geo.src\",\"size\":10}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "7ff2c4c0-4191-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:36.270Z", + "version": "WzI1MCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Filter Bytes Test: vega", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: vega\",\"type\":\"vega\",\"params\":{\"spec\":\"{ \\nconfig: { kibana: { renderer: \\\"svg\\\" }},\\n/*\\n\\nWelcome to Vega visualizations. Here you can design your own dataviz from scratch using a declarative language called Vega, or its simpler form Vega-Lite. In Vega, you have the full control of what data is loaded, even from multiple sources, how that data is transformed, and what visual elements are used to show it. Use help icon to view Vega examples, tutorials, and other docs. Use the wrench icon to reformat this text, or to remove comments.\\n\\nThis example graph shows the document count in all indexes in the current time range. You might need to adjust the time filter in the upper right corner.\\n*/\\n\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n title: Event counts from all indexes\\n\\n // Define the data source\\n data: {\\n url: {\\n/*\\nAn object instead of a string for the \\\"url\\\" param is treated as an Elasticsearch query. Anything inside this object is not part of the Vega language, but only understood by Kibana and Elasticsearch server. This query counts the number of documents per time interval, assuming you have a @timestamp field in your data.\\n\\nKibana has a special handling for the fields surrounded by \\\"%\\\". They are processed before the the query is sent to Elasticsearch. This way the query becomes context aware, and can use the time range and the dashboard filters.\\n*/\\n\\n // Apply dashboard context filters when set\\n %context%: true\\n // Filter the time picker (upper right corner) with this field\\n %timefield%: @timestamp\\n\\n/*\\nSee .search() documentation for : https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-search\\n*/\\n\\n // Which index to search\\n index: _all\\n // Aggregate data by the time field into time buckets, counting the number of documents in each bucket.\\n body: {\\n aggs: {\\n time_buckets: {\\n date_histogram: {\\n // Use date histogram aggregation on @timestamp field\\n field: @timestamp\\n // The interval value will depend on the daterange picker (true), or use an integer to set an approximate bucket count\\n interval: {%autointerval%: true}\\n // Make sure we get an entire range, even if it has no data\\n extended_bounds: {\\n // Use the current time range's start and end\\n min: {%timefilter%: \\\"min\\\"}\\n max: {%timefilter%: \\\"max\\\"}\\n }\\n // Use this for linear (e.g. line, area) graphs. Without it, empty buckets will not show up\\n min_doc_count: 0\\n }\\n }\\n }\\n // Speed up the response by only including aggregation results\\n size: 0\\n }\\n }\\n/*\\nElasticsearch will return results in this format:\\n\\naggregations: {\\n time_buckets: {\\n buckets: [\\n {\\n key_as_string: 2015-11-30T22:00:00.000Z\\n key: 1448920800000\\n doc_count: 0\\n },\\n {\\n key_as_string: 2015-11-30T23:00:00.000Z\\n key: 1448924400000\\n doc_count: 0\\n }\\n ...\\n ]\\n }\\n}\\n\\nFor our graph, we only need the list of bucket values. Use the format.property to discard everything else.\\n*/\\n format: {property: \\\"aggregations.time_buckets.buckets\\\"}\\n }\\n\\n // \\\"mark\\\" is the graphics element used to show our data. Other mark values are: area, bar, circle, line, point, rect, rule, square, text, and tick. See https://vega.github.io/vega-lite/docs/mark.html\\n mark: line\\n\\n // \\\"encoding\\\" tells the \\\"mark\\\" what data to use and in what way. See https://vega.github.io/vega-lite/docs/encoding.html\\n encoding: {\\n x: {\\n // The \\\"key\\\" value is the timestamp in milliseconds. Use it for X axis.\\n field: key\\n type: temporal\\n axis: {title: false} // Customize X axis format\\n }\\n y: {\\n // The \\\"doc_count\\\" is the count per bucket. Use it for Y axis.\\n field: doc_count\\n type: quantitative\\n axis: {title: \\\"Document count\\\"}\\n }\\n }\\n}\\n\"},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "8090dcb0-4195-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T19:28:21.967Z", + "version": "WzI2MSwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "last", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "846988b0-3dd4-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:05:59.867Z", + "version": "WzIwNywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"size.keyword\",\"value\":\"extra large\",\"params\":{\"query\":\"extra large\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"size.keyword\":{\"query\":\"extra large\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: non timebased line chart - dog data - with filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{\"field\":\"trainability\"},\"schema\":\"metric\",\"type\":\"max\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"breed.keyword\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"field\":\"barking level\"},\"schema\":\"metric\",\"type\":\"max\"},{\"enabled\":true,\"id\":\"4\",\"params\":{\"field\":\"activity level\"},\"schema\":\"metric\",\"type\":\"max\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Max trainability\"},\"drawLinesBetweenPoints\":true,\"mode\":\"normal\",\"show\":\"true\",\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"},{\"data\":{\"id\":\"3\",\"label\":\"Max barking level\"},\"drawLinesBetweenPoints\":true,\"mode\":\"normal\",\"show\":true,\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"},{\"data\":{\"id\":\"4\",\"label\":\"Max activity level\"},\"drawLinesBetweenPoints\":true,\"mode\":\"normal\",\"show\":true,\"showCircles\":true,\"type\":\"line\",\"valueAxis\":\"ValueAxis-1\"}],\"times\":[],\"type\":\"line\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Max trainability\"},\"type\":\"value\"}],\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"title\":\"Rendering Test: non timebased line chart - dog data - with filter\",\"type\":\"line\"}" + }, + "coreMigrationVersion": "8.0.1", + "id": "8bc8d6c0-3e6e-11e8-bbb9-e15942d5d48c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "f908c8e0-3e6d-11e8-bbb9-e15942d5d48c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "f908c8e0-3e6d-11e8-bbb9-e15942d5d48c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:31.173Z", + "version": "WzIyOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "* hi & $%!!@# 漢字 ^--=++[]{};'~`~<>?,./:\";'\\|\\\\ special chars", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "9b780cd0-3dd3-11e8-b2b9-5d5dc1715159", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [], + "type": "dashboard", + "updated_at": "2018-04-11T22:00:07.322Z", + "version": "WzE5MSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Filter Bytes Test: timelion split 5 on bytes", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: timelion split 5 on bytes\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(*, split=bytes:5)\",\"interval\":\"auto\"},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "9bebe980-4192-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:59:42.648Z", + "version": "WzI1NCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Filter Bytes Test: tsvb markdown", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: tsvb markdown\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"markdown\",\"series\":[{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"filters\",\"metrics\":[{\"id\":\"482d6560-4194-11e8-a461-7d278185cba4\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"terms_field\":\"clientip\",\"filter\":{\"query\":\"Filter Bytes Test:>1000\",\"language\":\"lucene\"},\"override_index_pattern\":0,\"series_index_pattern\":\"logstash-*\",\"series_time_field\":\"utc_time\",\"series_interval\":\"1m\",\"value_template\":\"\",\"split_filters\":[{\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"label\":\"\",\"color\":\"#68BC00\",\"id\":\"39a107e0-4194-11e8-a461-7d278185cba4\"}],\"label\":\"\",\"var_name\":\"\",\"split_color_mode\":\"gradient\",\"series_drop_last_bucket\":1}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"06893260-4194-11e8-a461-7d278185cba4\"}],\"bar_color_rules\":[{\"id\":\"36a0e740-4194-11e8-a461-7d278185cba4\"}],\"markdown\":\"{{bytes_1000.last.formatted}}\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "9fb4c670-4194-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T16:32:59.086Z", + "version": "WzI1NywxXQ==" +} + +{ + "attributes": { + "description": "I have two visualizations that are created off a non time based index", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":0,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"}]", + "timeRestore": false, + "title": "Non time based", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "a5d56330-3e6e-11e8-bbb9-e15942d5d48c", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "5e085850-3e6e-11e8-bbb9-e15942d5d48c", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "8bc8d6c0-3e6e-11e8-bbb9-e15942d5d48c", + "name": "2:panel_2", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-04-12T16:29:18.435Z", + "version": "WzIxMiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Filter Bytes Test: standard deviation heatmap with other bucket", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"-4,000 - 1,000\":\"rgb(247,252,245)\",\"1,000 - 6,000\":\"rgb(199,233,192)\",\"6,000 - 11,000\":\"rgb(116,196,118)\",\"11,000 - 16,000\":\"rgb(35,139,69)\"}}}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: standard deviation heatmap with other bucket\",\"type\":\"heatmap\",\"params\":{\"type\":\"heatmap\",\"addTooltip\":true,\"addLegend\":true,\"enableHover\":false,\"legendPosition\":\"right\",\"times\":[],\"colorsNumber\":4,\"colorSchema\":\"Greens\",\"setColorRange\":false,\"colorsRange\":[],\"invertColors\":false,\"percentageMode\":false,\"valueAxes\":[{\"show\":false,\"id\":\"ValueAxis-1\",\"type\":\"value\",\"scale\":{\"type\":\"linear\",\"defaultYExtents\":false},\"labels\":{\"show\":false,\"rotate\":0,\"overwriteColor\":false,\"color\":\"#555\"}}]},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"std_dev\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":true,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"_term\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "b3e70d00-4190-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:35.236Z", + "version": "WzI0NywxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Filter Bytes Test: max bytes guage percent mode", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 1\":\"rgb(0,104,55)\",\"1 - 15\":\"rgb(255,255,190)\",\"15 - 100\":\"rgb(165,0,38)\"}}}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: max bytes guage percent mode\",\"type\":\"gauge\",\"params\":{\"type\":\"gauge\",\"addTooltip\":true,\"addLegend\":true,\"isDisplayWarning\":false,\"gauge\":{\"extendRange\":true,\"percentageMode\":true,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"Labels\",\"colorsRange\":[{\"from\":0,\"to\":500},{\"from\":500,\"to\":7500},{\"from\":7500,\"to\":50000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":true,\"labels\":false,\"color\":\"#333\"},\"type\":\"meter\",\"style\":{\"bgWidth\":0.9,\"width\":0.9,\"mask\":false,\"bgMask\":false,\"maskBars\":50,\"bgFill\":\"#eee\",\"bgColor\":false,\"subText\":\"Im subtext\",\"fontSize\":60,\"labelColor\":true},\"alignment\":\"horizontal\"}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"bytes\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "c10c6b00-4191-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:36.267Z", + "version": "WzI0OCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"bytes\",\"value\":\"0\",\"params\":{\"query\":0,\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"bytes\":{\"query\":0,\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Filter Bytes Test: tag cloud with not 0 bytes filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Bytes Test: tag cloud with not 0 bytes filter\",\"type\":\"tagcloud\",\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"bytes\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "df72ad40-4194-11e8-bb13-d53698fb349a", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:36.276Z", + "version": "WzI1MSwxXQ==" +} + +{ + "attributes": { + "description": "Bytes bytes and more bytes", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":15,\"w\":17,\"h\":8,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":18,\"h\":13,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":37,\"w\":24,\"h\":12,\"i\":\"6\"},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":18,\"y\":30,\"w\":9,\"h\":7,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":28,\"y\":23,\"w\":15,\"h\":13,\"i\":\"8\"},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":43,\"w\":24,\"h\":15,\"i\":\"9\"},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":49,\"w\":18,\"h\":12,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":58,\"w\":24,\"h\":15,\"i\":\"11\"},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":61,\"w\":5,\"h\":4,\"i\":\"12\"},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":73,\"w\":17,\"h\":6,\"i\":\"13\"},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":65,\"w\":24,\"h\":15,\"i\":\"14\"},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":79,\"w\":24,\"h\":6,\"i\":\"15\"},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":80,\"w\":24,\"h\":15,\"i\":\"16\"},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":85,\"w\":13,\"h\":11,\"i\":\"17\"},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"x\":24,\"y\":95,\"w\":23,\"h\":11,\"i\":\"18\"},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"}]", + "refreshInterval": { + "display": "Off", + "pause": false, + "value": 0 + }, + "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", + "timeRestore": true, + "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", + "title": "All about those bytes", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "b60de070-4197-11e8-bb13-d53698fb349a", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "7ff2c4c0-4191-11e8-bb13-d53698fb349a", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "03d2afd0-4192-11e8-bb13-d53698fb349a", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "63983430-4192-11e8-bb13-d53698fb349a", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "0ca8c600-4195-11e8-bb13-d53698fb349a", + "name": "4:panel_4", + "type": "visualization" + }, + { + "id": "c10c6b00-4191-11e8-bb13-d53698fb349a", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "760a9060-4190-11e8-bb13-d53698fb349a", + "name": "6:panel_6", + "type": "visualization" + }, + { + "id": "1dcdfe30-4192-11e8-bb13-d53698fb349a", + "name": "7:panel_7", + "type": "visualization" + }, + { + "id": "584c0300-4191-11e8-bb13-d53698fb349a", + "name": "8:panel_8", + "type": "visualization" + }, + { + "id": "b3e70d00-4190-11e8-bb13-d53698fb349a", + "name": "9:panel_9", + "type": "visualization" + }, + { + "id": "df72ad40-4194-11e8-bb13-d53698fb349a", + "name": "10:panel_10", + "type": "visualization" + }, + { + "id": "9bebe980-4192-11e8-bb13-d53698fb349a", + "name": "11:panel_11", + "type": "visualization" + }, + { + "id": "9fb4c670-4194-11e8-bb13-d53698fb349a", + "name": "12:panel_12", + "type": "visualization" + }, + { + "id": "35417e50-4194-11e8-bb13-d53698fb349a", + "name": "13:panel_13", + "type": "visualization" + }, + { + "id": "039e4770-4194-11e8-bb13-d53698fb349a", + "name": "14:panel_14", + "type": "visualization" + }, + { + "id": "76c7f020-4194-11e8-bb13-d53698fb349a", + "name": "15:panel_15", + "type": "visualization" + }, + { + "id": "8090dcb0-4195-11e8-bb13-d53698fb349a", + "name": "16:panel_16", + "type": "visualization" + }, + { + "id": "29bd0240-4197-11e8-bb13-d53698fb349a", + "name": "17:panel_17", + "type": "visualization" + }, + { + "id": "55d37a30-4197-11e8-bb13-d53698fb349a", + "name": "18:panel_18", + "type": "search" + } + ], + "type": "dashboard", + "updated_at": "2018-04-16T17:00:48.503Z", + "version": "WzIxOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: timelion", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: timelion\",\"type\":\"timelion\",\"params\":{\"expression\":\".es(*, metric=avg:bytes, split=ip:5)\",\"interval\":\"auto\"},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "b92ae920-3dcc-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:31.110Z", + "version": "WzIyNCwxXQ==" +} + +{ + "attributes": { + "columns": [ + "agent", + "bytes", + "clientip" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Rendering Test: saved search", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "be5accf0-3dca-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2018-04-17T15:09:39.805Z", + "version": "WzI1MiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"animal.keyword\",\"value\":\"dog\",\"params\":{\"query\":\"dog\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"animal.keyword\":{\"query\":\"dog\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"language\":\"lucene\",\"query\":\"\"}}" + }, + "savedSearchRefName": "search_0", + "title": "Filter Test: animals: linked to search with filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Filter Test: animals: linked to search with filter\",\"type\":\"pie\",\"params\":{\"addLegend\":true,\"addTooltip\":true,\"isDonut\":true,\"labels\":{\"last_level\":true,\"show\":false,\"truncate\":100,\"values\":true},\"legendPosition\":\"right\",\"type\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"name.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "befdb6b0-3e59-11e8-9fc3-39e49624228e", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", + "name": "search_0", + "type": "search" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T17:16:27.743Z", + "version": "WzI1OCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: tsvb-ts", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: tsvb-ts\",\"type\":\"metrics\",\"params\":{\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"timeseries\",\"series\":[{\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"count\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"use_kibana_indexes\":false,\"drop_last_bucket\":1},\"aggs\":[]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "c40f4d40-3dcc-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:30.347Z", + "version": "WzIzNSwxXQ==" +} + +{ + "attributes": { + "columns": [ + "agent", + "bytes", + "clientip" + ], + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"meta\":{\"negate\":false,\"type\":\"phrase\",\"key\":\"bytes\",\"value\":\"1,607\",\"params\":{\"query\":1607,\"type\":\"phrase\"},\"disabled\":false,\"alias\":null,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"bytes\":{\"query\":1607,\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "Filter Bytes Test: search with filter", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "ca5ada40-3dca-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "search": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "search", + "updated_at": "2018-04-17T15:09:55.976Z", + "version": "WzI1MywxXQ==" +} + +{ + "attributes": { + "description": "", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"weightLbs:<50\",\"language\":\"lucene\"},\"filter\":[{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"name.keyword\",\"value\":\"Fee Fee\",\"params\":{\"query\":\"Fee Fee\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"name.keyword\":{\"query\":\"Fee Fee\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":true,\"useMargins\":true,\"hidePanelTitles\":true}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"}]", + "timeRestore": false, + "title": "bug", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "cbd3bc30-3e5a-11e8-9fc3-39e49624228e", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "a0f483a0-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + }, + { + "id": "771b4f10-3e59-11e8-9fc3-39e49624228e", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "befdb6b0-3e59-11e8-9fc3-39e49624228e", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "4c0c3f90-3e5a-11e8-9fc3-39e49624228e", + "name": "3:panel_3", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-04-12T14:07:12.243Z", + "version": "WzIwOCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: tsvb-metric", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: tsvb-metric\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"metric\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"sum_of_squares\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "cc43fab0-3dcc-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:30.353Z", + "version": "WzIzNCwxXQ==" +} + +{ + "attributes": { + "description": "I have one of every visualization type since the last time I was created!", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"}]", + "refreshInterval": { + "display": "Off", + "pause": false, + "value": 0 + }, + "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", + "timeRestore": true, + "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", + "title": "dashboard with table", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "d2525040-3dcd-11e8-8660-4d65aa086b3b", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", + "name": "3:panel_3", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-04-16T16:05:02.915Z", + "version": "WzIxNCwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[{\"meta\":{\"negate\":true,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.src\",\"value\":\"CN\",\"params\":{\"query\":\"CN\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.src\":{\"query\":\"CN\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: area with not filter", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: area with not filter\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100,\"filter\":true},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"fittingFunction\":\"zero\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"group\",\"params\":{\"filters\":[{\"input\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"label\":\"\"},{\"input\":{\"query\":\"bytes:>10\",\"language\":\"lucene\"}}]}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "e6140540-3dca-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + }, + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:33.165Z", + "version": "WzIzOSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Rendering Test: goal", + "uiStateJSON": "{\"vis\":{\"defaultColors\":{\"0 - 100\":\"rgb(0,104,55)\"}}}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: goal\",\"type\":\"goal\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"isDisplayWarning\":false,\"type\":\"gauge\",\"gauge\":{\"verticalSplit\":false,\"autoExtend\":false,\"percentageMode\":true,\"gaugeType\":\"Arc\",\"gaugeStyle\":\"Full\",\"backStyle\":\"Full\",\"orientation\":\"vertical\",\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"gaugeColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":4000}],\"invertColors\":false,\"labels\":{\"show\":true,\"color\":\"black\"},\"scale\":{\"show\":false,\"labels\":false,\"color\":\"#333\",\"width\":2},\"type\":\"meter\",\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"geo.src\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":2,\"order\":\"desc\",\"orderBy\":\"1\"}}]}" + }, + "coreMigrationVersion": "8.0.1", + "id": "ffa2e0c0-3dcb-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [ + { + "id": "0bf35f60-3dc9-11e8-8660-4d65aa086b3c", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2018-04-17T15:06:33.153Z", + "version": "WzIzNiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: tsvb-guage", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: tsvb-guage\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"gauge\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"d18e5970-3dcc-11e8-a2f6-c162ca6cf6ea\",\"color\":\"rgba(160,70,216,1)\",\"split_mode\":\"filter\",\"metrics\":[{\"id\":\"d18e5971-3dcc-11e8-a2f6-c162ca6cf6ea\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"bar_color_rules\":[{\"id\":\"cd25a820-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_color_rules\":[{\"id\":\"e0be22e0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_width\":10,\"gauge_inner_width\":10,\"gauge_style\":\"half\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "e4d8b430-3dcc-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:31.106Z", + "version": "WzIyNSwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: tsvb-markdown", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: tsvb-markdown\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"markdown\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"d18e5970-3dcc-11e8-a2f6-c162ca6cf6ea\",\"color\":\"rgba(160,70,216,1)\",\"split_mode\":\"filter\",\"metrics\":[{\"id\":\"d18e5971-3dcc-11e8-a2f6-c162ca6cf6ea\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"bar_color_rules\":[{\"id\":\"cd25a820-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_color_rules\":[{\"id\":\"e0be22e0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"gauge_width\":10,\"gauge_inner_width\":10,\"gauge_style\":\"half\",\"markdown\":\"\\nHi Avg last bytes: {{ average_of_bytes.last.raw }}\",\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "f81134a0-3dcc-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:30.355Z", + "version": "WzIzMiwxXQ==" +} + +{ + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Rendering Test: tsvb-topn", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Rendering Test: tsvb-topn\",\"type\":\"metrics\",\"aggs\":[],\"params\":{\"time_range_mode\":\"last_value\",\"id\":\"61ca57f0-469d-11e7-af02-69e470af7417\",\"type\":\"top_n\",\"series\":[{\"time_range_mode\":\"entire_time_range\",\"id\":\"61ca57f1-469d-11e7-af02-69e470af7417\",\"color\":\"#68BC00\",\"split_mode\":\"everything\",\"metrics\":[{\"id\":\"61ca57f2-469d-11e7-af02-69e470af7417\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"split_color_mode\":\"gradient\"},{\"time_range_mode\":\"entire_time_range\",\"id\":\"d18e5970-3dcc-11e8-a2f6-c162ca6cf6ea\",\"color\":\"rgba(160,70,216,1)\",\"split_mode\":\"filter\",\"metrics\":[{\"id\":\"d18e5971-3dcc-11e8-a2f6-c162ca6cf6ea\",\"type\":\"avg\",\"field\":\"bytes\"}],\"seperate_axis\":0,\"axis_position\":\"right\",\"formatter\":\"number\",\"chart_type\":\"line\",\"line_width\":1,\"point_size\":1,\"fill\":0.5,\"stacked\":\"none\",\"filter\":{\"query\":\"bytes:>1000\",\"language\":\"lucene\"},\"split_color_mode\":\"gradient\"}],\"time_field\":\"@timestamp\",\"index_pattern\":\"logstash-*\",\"interval\":\"auto\",\"axis_position\":\"left\",\"axis_formatter\":\"number\",\"show_legend\":1,\"show_grid\":1,\"background_color_rules\":[{\"id\":\"c50bd5b0-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"bar_color_rules\":[{\"id\":\"cd25a820-3dcc-11e8-a2f6-c162ca6cf6ea\"}],\"use_kibana_indexes\":false,\"hide_last_value_indicator\":true,\"axis_scale\":\"normal\",\"truncate_legend\":1,\"max_lines_legend\":1,\"tooltip_mode\":\"show_all\",\"drop_last_bucket\":1,\"isModelInvalid\":false}}" + }, + "coreMigrationVersion": "8.0.1", + "id": "df815d20-3dcc-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "visualization": "8.0.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2018-04-17T15:06:30.349Z", + "version": "WzIzMywxXQ==" +} + +{ + "attributes": { + "description": "I have one of every visualization type since the last time I was created!", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":30,\"w\":24,\"h\":15,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":30,\"w\":24,\"h\":15,\"i\":\"6\"},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":45,\"w\":24,\"h\":15,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":45,\"w\":24,\"h\":15,\"i\":\"8\"},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":60,\"w\":24,\"h\":15,\"i\":\"9\"},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":60,\"w\":24,\"h\":15,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":75,\"w\":24,\"h\":15,\"i\":\"11\"},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":75,\"w\":24,\"h\":15,\"i\":\"12\"},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":90,\"w\":24,\"h\":15,\"i\":\"13\"},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":105,\"w\":24,\"h\":15,\"i\":\"15\"},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":105,\"w\":24,\"h\":15,\"i\":\"16\"},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":120,\"w\":24,\"h\":15,\"i\":\"17\"},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":120,\"w\":24,\"h\":15,\"i\":\"18\"},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":135,\"w\":24,\"h\":15,\"i\":\"19\"},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":135,\"w\":24,\"h\":15,\"i\":\"20\"},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":150,\"w\":24,\"h\":15,\"i\":\"21\"},\"panelIndex\":\"21\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_21\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":150,\"w\":24,\"h\":15,\"i\":\"22\"},\"panelIndex\":\"22\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_22\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":165,\"w\":24,\"h\":15,\"i\":\"23\"},\"panelIndex\":\"23\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_23\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"x\":24,\"y\":165,\"w\":24,\"h\":15,\"i\":\"24\"},\"panelIndex\":\"24\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_24\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":180,\"w\":24,\"h\":15,\"i\":\"25\"},\"panelIndex\":\"25\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_25\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"x\":24,\"y\":180,\"w\":24,\"h\":15,\"i\":\"26\"},\"panelIndex\":\"26\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_26\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":195,\"w\":24,\"h\":15,\"i\":\"27\"},\"panelIndex\":\"27\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_27\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":195,\"w\":24,\"h\":15,\"i\":\"28\"},\"panelIndex\":\"28\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_28\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":210,\"w\":24,\"h\":15,\"i\":\"29\"},\"panelIndex\":\"29\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_29\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":210,\"i\":\"30\"},\"panelIndex\":\"30\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_30\"}]", + "refreshInterval": { + "display": "Off", + "pause": false, + "value": 0 + }, + "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", + "timeRestore": true, + "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", + "title": "dashboard with everything", + "version": 1 + }, + "coreMigrationVersion": "8.0.1", + "id": "d2525040-3dcd-11e8-8660-4d65aa086b3c", + "migrationVersion": { + "dashboard": "8.0.1" + }, + "references": [ + { + "id": "e6140540-3dca-11e8-8660-4d65aa086b3c", + "name": "1:panel_1", + "type": "visualization" + }, + { + "id": "3525b840-3dcb-11e8-8660-4d65aa086b3c", + "name": "2:panel_2", + "type": "visualization" + }, + { + "id": "4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", + "name": "3:panel_3", + "type": "visualization" + }, + { + "id": "ffa2e0c0-3dcb-11e8-8660-4d65aa086b3c", + "name": "5:panel_5", + "type": "visualization" + }, + { + "id": "e2023110-3dcb-11e8-8660-4d65aa086b3c", + "name": "6:panel_6", + "type": "visualization" + }, + { + "id": "145ced90-3dcb-11e8-8660-4d65aa086b3c", + "name": "7:panel_7", + "type": "visualization" + }, + { + "id": "2d1b1620-3dcd-11e8-8660-4d65aa086b3c", + "name": "8:panel_8", + "type": "visualization" + }, + { + "id": "42535e30-3dcd-11e8-8660-4d65aa086b3c", + "name": "9:panel_9", + "type": "visualization" + }, + { + "id": "42535e30-3dcd-11e8-8660-4d65aa086b3c", + "name": "10:panel_10", + "type": "visualization" + }, + { + "id": "4c0f47e0-3dcd-11e8-8660-4d65aa086b3c", + "name": "11:panel_11", + "type": "visualization" + }, + { + "id": "11ae2bd0-3dcc-11e8-8660-4d65aa086b3c", + "name": "12:panel_12", + "type": "visualization" + }, + { + "id": "3fe22200-3dcb-11e8-8660-4d65aa086b3c", + "name": "13:panel_13", + "type": "visualization" + }, + { + "id": "78803be0-3dcd-11e8-8660-4d65aa086b3c", + "name": "15:panel_15", + "type": "visualization" + }, + { + "id": "b92ae920-3dcc-11e8-8660-4d65aa086b3c", + "name": "16:panel_16", + "type": "visualization" + }, + { + "id": "e4d8b430-3dcc-11e8-8660-4d65aa086b3c", + "name": "17:panel_17", + "type": "visualization" + }, + { + "id": "f81134a0-3dcc-11e8-8660-4d65aa086b3c", + "name": "18:panel_18", + "type": "visualization" + }, + { + "id": "cc43fab0-3dcc-11e8-8660-4d65aa086b3c", + "name": "19:panel_19", + "type": "visualization" + }, + { + "id": "02a2e4e0-3dcd-11e8-8660-4d65aa086b3c", + "name": "20:panel_20", + "type": "visualization" + }, + { + "id": "df815d20-3dcc-11e8-8660-4d65aa086b3c", + "name": "21:panel_21", + "type": "visualization" + }, + { + "id": "c40f4d40-3dcc-11e8-8660-4d65aa086b3c", + "name": "22:panel_22", + "type": "visualization" + }, + { + "id": "7fda8ee0-3dcd-11e8-8660-4d65aa086b3c", + "name": "23:panel_23", + "type": "visualization" + }, + { + "id": "a16d1990-3dca-11e8-8660-4d65aa086b3c", + "name": "24:panel_24", + "type": "search" + }, + { + "id": "be5accf0-3dca-11e8-8660-4d65aa086b3c", + "name": "25:panel_25", + "type": "search" + }, + { + "id": "ca5ada40-3dca-11e8-8660-4d65aa086b3c", + "name": "26:panel_26", + "type": "search" + }, + { + "id": "771b4f10-3e59-11e8-9fc3-39e49624228e", + "name": "27:panel_27", + "type": "visualization" + }, + { + "id": "5e085850-3e6e-11e8-bbb9-e15942d5d48c", + "name": "28:panel_28", + "type": "visualization" + }, + { + "id": "8bc8d6c0-3e6e-11e8-bbb9-e15942d5d48c", + "name": "29:panel_29", + "type": "visualization" + }, + { + "id": "befdb6b0-3e59-11e8-9fc3-39e49624228e", + "name": "30:panel_30", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-04-16T16:05:02.915Z", + "version": "WzIxMywxXQ==" +} + +{ + "attributes": { + "fieldFormatMap": "{\"machine.ram\":{\"id\":\"number\",\"params\":{\"pattern\":\"0,0.[000] b\"}}}", + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "to-be-deleted" + }, + "coreMigrationVersion": "7.14.0", + "id": "1b1789d0-9e93-11ea-853e-adc0effaf76d", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-04-16T16:57:12.263Z", + "version": "WzE3NiwxXQ==" +} + +{ + "attributes": { + "fieldFormatMap": "{\"machine.ram\":{\"id\":\"number\",\"params\":{\"pattern\":\"0,0.[000] b\"}}}", + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "to-be-deleted" + }, + "coreMigrationVersion": "7.14.0", + "id": "a0f483a0-3dc9-11e8-8660-bad-index", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-04-16T16:57:12.263Z", + "version": "WzE3NiwxXQ==" +} \ No newline at end of file diff --git a/test/functional/fixtures/kbn_archiver/dashboard/current/kibana_unload.json b/test/functional/fixtures/kbn_archiver/dashboard/current/kibana_unload.json new file mode 100644 index 0000000000000..d2543cf42a6d2 --- /dev/null +++ b/test/functional/fixtures/kbn_archiver/dashboard/current/kibana_unload.json @@ -0,0 +1,35 @@ +{ + "attributes": { + "fieldFormatMap": "{\"machine.ram\":{\"id\":\"number\",\"params\":{\"pattern\":\"0,0.[000] b\"}}}", + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "to-be-deleted" + }, + "coreMigrationVersion": "7.14.0", + "id": "1b1789d0-9e93-11ea-853e-adc0effaf76d", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-04-16T16:57:12.263Z", + "version": "WzE3NiwxXQ==" +} + +{ + "attributes": { + "fieldFormatMap": "{\"machine.ram\":{\"id\":\"number\",\"params\":{\"pattern\":\"0,0.[000] b\"}}}", + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":2,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.keyword\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "to-be-deleted" + }, + "coreMigrationVersion": "7.14.0", + "id": "a0f483a0-3dc9-11e8-8660-bad-index", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "updated_at": "2018-04-16T16:57:12.263Z", + "version": "WzE3NiwxXQ==" +} \ No newline at end of file diff --git a/test/functional/fixtures/kbn_archiver/dashboard/legacy.json b/test/functional/fixtures/kbn_archiver/dashboard/legacy.json new file mode 100644 index 0000000000000..0a05af3b40bb0 --- /dev/null +++ b/test/functional/fixtures/kbn_archiver/dashboard/legacy.json @@ -0,0 +1,241 @@ +{ + "attributes": { + "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", + "timeFieldName": "@timestamp", + "title": "logstash-*" + }, + "coreMigrationVersion": "7.17.1", + "id": "logstash-*", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "version": "WzksMl0=" +} + +{ + "attributes": { + "description": "InputControl", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{}" + }, + "title": "Visualization InputControl", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"logstash control panel\",\"type\":\"input_control_vis\",\"params\":{\"controls\":[{\"id\":\"1508761640807\",\"fieldName\":\"machine.os.raw\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_0_index_pattern\"},{\"id\":\"1508761655907\",\"fieldName\":\"bytes\",\"label\":\"\",\"type\":\"range\",\"options\":{\"decimalPlaces\":0,\"step\":512},\"indexPatternRefName\":\"control_1_index_pattern\"},{\"id\":\"1510594169532\",\"fieldName\":\"geo.dest\",\"label\":\"\",\"type\":\"list\",\"options\":{\"type\":\"terms\",\"multiselect\":true,\"size\":5,\"order\":\"desc\"},\"indexPatternRefName\":\"control_2_index_pattern\"}],\"updateFiltersOnChange\":false},\"aggs\":[]}" + }, + "coreMigrationVersion": "7.17.1", + "id": "Visualization-InputControl", + "migrationVersion": { + "visualization": "7.17.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "control_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "logstash-*", + "name": "control_1_index_pattern", + "type": "index-pattern" + }, + { + "id": "logstash-*", + "name": "control_2_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzEwLDJd" +} + +{ + "attributes": { + "description": "MetricChart", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Visualization MetricChart", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"New Visualization\",\"type\":\"metric\",\"params\":{\"handleNoResults\":true,\"fontSize\":60},\"aggs\":[{\"id\":\"1\",\"type\":\"percentile_ranks\",\"schema\":\"metric\",\"params\":{\"field\":\"memory\",\"values\":[99]}}],\"listeners\":{}}" + }, + "coreMigrationVersion": "7.17.1", + "id": "Visualization-MetricChart", + "migrationVersion": { + "visualization": "7.17.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzE2LDJd" +} + +{ + "attributes": { + "description": "PieChart", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Visualization PieChart", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"New Visualization\",\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"memory\",\"interval\":40000,\"extended_bounds\":{}}}],\"listeners\":{}}" + }, + "coreMigrationVersion": "7.17.1", + "id": "Visualization-PieChart", + "migrationVersion": { + "visualization": "7.17.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzE0LDJd" +} + +{ + "attributes": { + "description": "TileMap", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Visualization TileMap", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"New Visualization\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatRadius\":25,\"heatBlur\":15,\"heatNormalizeData\":true,\"wms\":{\"enabled\":false,\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\",\"options\":{\"version\":\"1.3.0\",\"layers\":\"0\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"Maps provided by USGS\",\"styles\":\"\"}}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.coordinates\",\"autoPrecision\":true,\"precision\":2}}],\"listeners\":{}}" + }, + "coreMigrationVersion": "7.17.1", + "id": "Visualization-TileMap", + "migrationVersion": { + "visualization": "7.17.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzEzLDJd" +} + +{ + "attributes": { + "description": "VerticalBarChart", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Visualization☺ VerticalBarChart", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"New Visualization\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}" + }, + "coreMigrationVersion": "7.17.1", + "id": "Visualization☺-VerticalBarChart", + "migrationVersion": { + "visualization": "7.17.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzEyLDJd" +} + +{ + "attributes": { + "description": "DataTable", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Visualization☺漢字 DataTable", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"New Visualization\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"showToolbar\":true},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"histogram\",\"schema\":\"bucket\",\"params\":{\"field\":\"bytes\",\"interval\":2000,\"extended_bounds\":{}}}],\"listeners\":{}}" + }, + "coreMigrationVersion": "7.17.1", + "id": "Visualization☺漢字-DataTable", + "migrationVersion": { + "visualization": "7.17.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzExLDJd" +} + +{ + "attributes": { + "description": "AreaChart", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Visualization漢字 AreaChart", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"New Visualization\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}" + }, + "coreMigrationVersion": "7.17.1", + "id": "Visualization漢字-AreaChart", + "migrationVersion": { + "visualization": "7.17.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzE3LDJd" +} + +{ + "attributes": { + "description": "LineChart", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Visualization漢字 LineChart", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"New Visualization\",\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{},\"row\":false},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"extension.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}" + }, + "coreMigrationVersion": "7.17.1", + "id": "Visualization漢字-LineChart", + "migrationVersion": { + "visualization": "7.17.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzE1LDJd" +} \ No newline at end of file diff --git a/test/plugin_functional/test_suites/data_plugin/session.ts b/test/plugin_functional/test_suites/data_plugin/session.ts index 54b86f0c8060b..e119d60a90244 100644 --- a/test/plugin_functional/test_suites/data_plugin/session.ts +++ b/test/plugin_functional/test_suites/data_plugin/session.ts @@ -15,6 +15,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide const testSubjects = getService('testSubjects'); const toasts = getService('toasts'); const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const getSessionIds = async () => { const sessionsBtn = await testSubjects.find('showSessionsButton'); @@ -73,8 +74,9 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide await esArchiver.loadIfNeeded( 'test/functional/fixtures/es_archiver/dashboard/current/data' ); - await esArchiver.loadIfNeeded( - 'test/functional/fixtures/es_archiver/dashboard/current/kibana' + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' ); await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.loadSavedDashboard('dashboard with filter'); @@ -88,7 +90,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide after(async () => { await esArchiver.unload('test/functional/fixtures/es_archiver/dashboard/current/data'); - await esArchiver.unload('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); }); it('on load there is a single session', async () => { diff --git a/test/plugin_functional/test_suites/panel_actions/index.js b/test/plugin_functional/test_suites/panel_actions/index.js index 70e6ed67b218c..13958f758e1a9 100644 --- a/test/plugin_functional/test_suites/panel_actions/index.js +++ b/test/plugin_functional/test_suites/panel_actions/index.js @@ -15,7 +15,10 @@ export default function ({ getService, getPageObjects, loadTestFile }) { describe('pluggable panel actions', function () { before(async () => { await browser.setWindowSize(1300, 900); - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*', @@ -26,7 +29,7 @@ export default function ({ getService, getPageObjects, loadTestFile }) { after(async function () { await PageObjects.dashboard.clearSavedObjectsFromAppLinks(); - await esArchiver.unload('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.unload('test/functional/fixtures/es_archiver/dashboard/current/data'); }); diff --git a/x-pack/test/functional/apps/canvas/embeddables/lens.ts b/x-pack/test/functional/apps/canvas/embeddables/lens.ts index cfb8e4fcd92f6..5ecd3a3156909 100644 --- a/x-pack/test/functional/apps/canvas/embeddables/lens.ts +++ b/x-pack/test/functional/apps/canvas/embeddables/lens.ts @@ -24,6 +24,7 @@ export default function canvasLensTest({ getService, getPageObjects }: FtrProvid describe('lens in canvas', function () { before(async () => { await esArchiver.load(archives.es); + await kibanaServer.savedObjects.cleanStandardList(); await kibanaServer.importExport.load(archives.kbn); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-lens' }); // open canvas home @@ -36,7 +37,7 @@ export default function canvasLensTest({ getService, getPageObjects }: FtrProvid after(async () => { await esArchiver.unload(archives.es); - await kibanaServer.importExport.unload(archives.kbn); + await kibanaServer.savedObjects.cleanStandardList(); }); describe('by-reference', () => { diff --git a/x-pack/test/functional/apps/canvas/embeddables/maps.ts b/x-pack/test/functional/apps/canvas/embeddables/maps.ts index 1d325259fb16c..3a2034b7dfecf 100644 --- a/x-pack/test/functional/apps/canvas/embeddables/maps.ts +++ b/x-pack/test/functional/apps/canvas/embeddables/maps.ts @@ -13,9 +13,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardPanelActions = getService('dashboardPanelActions'); const dashboardAddPanel = getService('dashboardAddPanel'); const testSubjects = getService('testSubjects'); + const kibanaServer = getService('kibanaServer'); describe('maps in canvas', function () { before(async () => { + await kibanaServer.savedObjects.cleanStandardList(); // open canvas home await PageObjects.common.navigateToApp('canvas'); // create new workpad diff --git a/x-pack/test/functional/apps/canvas/embeddables/saved_search.ts b/x-pack/test/functional/apps/canvas/embeddables/saved_search.ts index d2e640ef9b9b8..6024b4114199a 100644 --- a/x-pack/test/functional/apps/canvas/embeddables/saved_search.ts +++ b/x-pack/test/functional/apps/canvas/embeddables/saved_search.ts @@ -10,16 +10,16 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['canvas', 'common', 'header', 'discover']); const testSubjects = getService('testSubjects'); - const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const dashboardAddPanel = getService('dashboardAddPanel'); const dashboardPanelActions = getService('dashboardPanelActions'); - const archives = { - es: 'test/functional/fixtures/es_archiver/dashboard/current/kibana', - }; describe('saved search in canvas', function () { before(async () => { - await esArchiver.load(archives.es); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); // open canvas home await PageObjects.common.navigateToApp('canvas'); // create new workpad @@ -28,7 +28,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); after(async () => { - await esArchiver.unload(archives.es); + await kibanaServer.savedObjects.cleanStandardList(); }); describe('by-reference', () => { diff --git a/x-pack/test/functional/apps/canvas/embeddables/visualization.ts b/x-pack/test/functional/apps/canvas/embeddables/visualization.ts index d8d851e9708e6..09f2fba9810f3 100644 --- a/x-pack/test/functional/apps/canvas/embeddables/visualization.ts +++ b/x-pack/test/functional/apps/canvas/embeddables/visualization.ts @@ -11,17 +11,24 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const PageObjects = getPageObjects(['canvas', 'common', 'header', 'visualize']); - const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); const dashboardAddPanel = getService('dashboardAddPanel'); const dashboardPanelActions = getService('dashboardPanelActions'); - const archives = { - es: 'test/functional/fixtures/es_archiver/dashboard/current/kibana', - }; describe('visualization in canvas', function () { before(async () => { - await esArchiver.load(archives.es); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' + ); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/canvas/lens' + ); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana_unload' + ); + // open canvas home await PageObjects.common.navigateToApp('canvas'); // create new workpad @@ -30,7 +37,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); after(async () => { - await esArchiver.unload(archives.es); + await kibanaServer.savedObjects.cleanStandardList(); }); describe('by-reference', () => { diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts index a47ebeac3861a..5060ac60eceae 100644 --- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts +++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts @@ -26,7 +26,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); after(async () => { - await kibanaServer.savedObjects.clean({ types: ['canvas-workpad'] }); + await kibanaServer.savedObjects.cleanStandardList(); await soInfo.logSoTypes(log); }); diff --git a/x-pack/test/functional/apps/canvas/smoke_test.js b/x-pack/test/functional/apps/canvas/smoke_test.js index d2ec930579c92..053c58e88cde1 100644 --- a/x-pack/test/functional/apps/canvas/smoke_test.js +++ b/x-pack/test/functional/apps/canvas/smoke_test.js @@ -26,7 +26,7 @@ export default function canvasSmokeTest({ getService, getPageObjects }) { }); after(async () => { - await kibanaServer.importExport.unload(archive); + await kibanaServer.savedObjects.cleanStandardList(); }); it('loads workpad list', async () => { diff --git a/x-pack/test/functional/apps/dashboard/dashboard_maps_by_value.ts b/x-pack/test/functional/apps/dashboard/dashboard_maps_by_value.ts index f8a90767b54ac..e885d1c0d3f73 100644 --- a/x-pack/test/functional/apps/dashboard/dashboard_maps_by_value.ts +++ b/x-pack/test/functional/apps/dashboard/dashboard_maps_by_value.ts @@ -88,9 +88,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await kibanaServer.importExport.unload( 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' ); - await kibanaServer.savedObjects.clean({ - types: ['search', 'index-pattern', 'visualization', 'dashboard', 'tag', 'map'], - }); + await kibanaServer.savedObjects.cleanStandardList(); }); describe('adding a map by value', () => { diff --git a/x-pack/test/functional/apps/dashboard/dashboard_tagging.ts b/x-pack/test/functional/apps/dashboard/dashboard_tagging.ts index 7a358e24a2b41..784d1f3678415 100644 --- a/x-pack/test/functional/apps/dashboard/dashboard_tagging.ts +++ b/x-pack/test/functional/apps/dashboard/dashboard_tagging.ts @@ -81,9 +81,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.unload( 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' ); - await kibanaServer.savedObjects.clean({ - types: ['search', 'index-pattern', 'visualization', 'dashboard', 'tag'], - }); + await kibanaServer.savedObjects.cleanStandardList(); }); it('adds a new tag to a new Dashboard', async () => { diff --git a/x-pack/test/functional/apps/dashboard/panel_titles.ts b/x-pack/test/functional/apps/dashboard/panel_titles.ts index 20c1d745b5548..3db72ba8d0a90 100644 --- a/x-pack/test/functional/apps/dashboard/panel_titles.ts +++ b/x-pack/test/functional/apps/dashboard/panel_titles.ts @@ -30,7 +30,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('panel titles', () => { before(async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); + await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.importExport.load( 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json' diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/usage.ts b/x-pack/test/reporting_api_integration/reporting_and_security/usage.ts index 628dcd7d266bd..b600e089f1a87 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/usage.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/usage.ts @@ -24,7 +24,7 @@ const JOB_PARAMS_CSV_DEFAULT_SPACE = `,filter:!((meta:(field:order_date,index:aac3e500-f2c7-11ea-8250-fb138aa491e7,params:()),query:(range:(order_date:(format:strict_date_optional_time,gte:'2019-06-02T12:28:40.866Z'` + `,lte:'2019-07-18T20:59:57.136Z'))))),index:aac3e500-f2c7-11ea-8250-fb138aa491e7,parent:(filter:!(),highlightAll:!t,index:aac3e500-f2c7-11ea-8250-fb138aa491e7` + `,query:(language:kuery,query:''),version:!t),sort:!((order_date:desc)),trackTotalHits:!t)`; -const OSS_KIBANA_ARCHIVE_PATH = 'test/functional/fixtures/es_archiver/dashboard/current/kibana'; +const OSS_KIBANA_ARCHIVE_PATH = 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana'; const OSS_DATA_ARCHIVE_PATH = 'test/functional/fixtures/es_archiver/dashboard/current/data'; interface UsageStats { @@ -34,6 +34,7 @@ interface UsageStats { // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const reportingAPI = getService('reportingAPI'); const retry = getService('retry'); const usageAPI = getService('usageAPI'); @@ -117,13 +118,14 @@ export default function ({ getService }: FtrProviderContext) { describe('from new jobs posted', () => { before(async () => { - await esArchiver.load(OSS_KIBANA_ARCHIVE_PATH); + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.importExport.load(OSS_KIBANA_ARCHIVE_PATH); await esArchiver.load(OSS_DATA_ARCHIVE_PATH); await reportingAPI.initEcommerce(); }); after(async () => { - await esArchiver.unload(OSS_KIBANA_ARCHIVE_PATH); + await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.unload(OSS_DATA_ARCHIVE_PATH); await reportingAPI.teardownEcommerce(); }); From b7f3271faeae9d71bf523e84ab47114c75d4a00c Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 9 Mar 2022 15:44:24 +0100 Subject: [PATCH 118/140] [Lens] Improve Datatable content height with custom row height (#127134) * :sparkles: Add new rowHeight feature * :white_check_mark: Add unit tests for new feature * :card_file_box: Update migration logic * :label: Fix type issue * :white_check_mark: fix usual map migration test * Update x-pack/plugins/lens/public/datatable_visualization/components/toolbar.tsx * :bug: Enable word wrap for multi lines * :white_check_mark: Fix unit test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/expressions/datatable/datatable.ts | 5 + .../__snapshots__/table_basic.test.tsx.snap | 21 +++ .../components/cell_value.test.tsx | 3 +- .../components/cell_value.tsx | 5 +- .../components/table_basic.test.tsx | 2 + .../components/table_basic.tsx | 23 +++- .../components/toolbar.test.tsx | 80 +++++++++--- .../components/toolbar.tsx | 116 ++++++++++++++--- .../visualization.test.tsx | 47 ++++++- .../datatable_visualization/visualization.tsx | 8 +- .../make_lens_embeddable_factory.ts | 11 ++ .../server/migrations/common_migrations.ts | 16 +++ .../saved_object_migrations.test.ts | 120 ++++++++++++++++++ .../migrations/saved_object_migrations.ts | 7 + .../plugins/lens/server/migrations/types.ts | 11 ++ .../api_integration/apis/maps/migrations.js | 2 +- 16 files changed, 426 insertions(+), 51 deletions(-) diff --git a/x-pack/plugins/lens/common/expressions/datatable/datatable.ts b/x-pack/plugins/lens/common/expressions/datatable/datatable.ts index 1eab399055480..af98485a1bcaf 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/datatable.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/datatable.ts @@ -28,6 +28,7 @@ export interface DatatableArgs { sortingColumnId: SortingState['columnId']; sortingDirection: SortingState['direction']; fitRowToContent?: boolean; + rowHeightLines?: number; pageSize?: PagingState['size']; } @@ -68,6 +69,10 @@ export const getDatatable = ( types: ['boolean'], help: '', }, + rowHeightLines: { + types: ['number'], + help: '', + }, pageSize: { types: ['number'], help: '', diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap b/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap index 40eb546dfc208..6100bc204d45b 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap +++ b/x-pack/plugins/lens/public/datatable_visualization/components/__snapshots__/table_basic.test.tsx.snap @@ -227,6 +227,13 @@ exports[`DatatableComponent it renders actions column when there are row actions onColumnResize={[Function]} renderCellValue={[Function]} rowCount={1} + rowHeightsOptions={ + Object { + "defaultHeight": Object { + "lineCount": 1, + }, + } + } sorting={ Object { "columns": Array [], @@ -472,6 +479,13 @@ exports[`DatatableComponent it renders the title and value 1`] = ` onColumnResize={[Function]} renderCellValue={[Function]} rowCount={1} + rowHeightsOptions={ + Object { + "defaultHeight": Object { + "lineCount": 1, + }, + } + } sorting={ Object { "columns": Array [], @@ -712,6 +726,13 @@ exports[`DatatableComponent it should render hide, reset, and sort actions on he onColumnResize={[Function]} renderCellValue={[Function]} rowCount={1} + rowHeightsOptions={ + Object { + "defaultHeight": Object { + "lineCount": 1, + }, + } + } sorting={ Object { "columns": Array [], diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx index bd35874bc352b..f383c13e275aa 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx @@ -86,7 +86,7 @@ describe('datatable cell renderer', () => { /> ); - expect(cell.find('.lnsTableCell').prop('className')).toContain('--right'); + expect(cell.find('.lnsTableCell--right').exists()).toBeTruthy(); }); describe('dynamic coloring', () => { @@ -127,6 +127,7 @@ describe('datatable cell renderer', () => { ], sortingColumnId: '', sortingDirection: 'none', + rowHeightLines: 1, }; } diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx index a493ed2eb1693..9ecd7579aa17f 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.tsx @@ -20,7 +20,8 @@ export const createGridCell = ( columnConfig: ColumnConfig, DataContext: React.Context, uiSettings: IUiSettingsClient, - fitRowToContent?: boolean + fitRowToContent?: boolean, + rowHeight?: number ) => { // Changing theme requires a full reload of the page, so we can cache here const IS_DARK_THEME = uiSettings.get('theme:darkMode'); @@ -31,7 +32,7 @@ export const createGridCell = ( const currentAlignment = alignments && alignments[columnId]; const alignmentClassName = `lnsTableCell--${currentAlignment}`; const className = classNames(alignmentClassName, { - lnsTableCell: !fitRowToContent, + lnsTableCell: !fitRowToContent && rowHeight === 1, }); const { colorMode, palette } = diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx index 6cab22cd08ccd..36fd1581cb9b6 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx @@ -84,6 +84,7 @@ function sampleArgs() { ], sortingColumnId: '', sortingDirection: 'none', + rowHeightLines: 1, }; return { data, args }; @@ -299,6 +300,7 @@ describe('DatatableComponent', () => { ], sortingColumnId: '', sortingDirection: 'none', + rowHeightLines: 1, }; const wrapper = mountWithIntl( diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx index 403858f4ba48c..45ceb5854152e 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.tsx @@ -334,9 +334,16 @@ export const DatatableComponent = (props: DatatableRenderProps) => { columnConfig, DataContext, props.uiSettings, - props.args.fitRowToContent + props.args.fitRowToContent, + props.args.rowHeightLines ), - [formatters, columnConfig, props.uiSettings, props.args.fitRowToContent] + [ + formatters, + columnConfig, + props.uiSettings, + props.args.fitRowToContent, + props.args.rowHeightLines, + ] ); const columnVisibility = useMemo( @@ -414,13 +421,15 @@ export const DatatableComponent = (props: DatatableRenderProps) => { { + const original = jest.requireActual('@elastic/eui'); + + return { + ...original, + htmlIdGenerator: (fn: unknown) => { + return () => ''; + }, + }; +}); class Harness { wrapper: ReactWrapper; @@ -25,12 +38,25 @@ class Harness { this.wrapper.find(ToolbarButton).simulate('click'); } - public get fitRowToContentSwitch() { - return this.wrapper.find('EuiSwitch[data-test-subj="lens-legend-auto-height-switch"]'); + public get rowHeight() { + return this.wrapper.find(EuiButtonGroup); } - toggleFitRowToContent() { - this.fitRowToContentSwitch.prop('onChange')!({} as FormEvent); + changeRowHeight(newMode: 'single' | 'auto' | 'custom') { + this.rowHeight.prop('onChange')!(newMode); + } + + public get rowHeightLines() { + return this.wrapper.find(EuiRange); + } + + changeRowHeightLines(lineCount: number) { + this.rowHeightLines.prop('onChange')!( + { + currentTarget: { value: lineCount }, + } as unknown as ChangeEvent, + true + ); } public get paginationSwitch() { @@ -56,7 +82,7 @@ describe('datatable toolbar', () => { setState: jest.fn(), frame: {} as FramePublicAPI, state: { - fitRowToContent: false, + rowHeight: 'single', } as DatatableVisualizationState, }; @@ -66,37 +92,55 @@ describe('datatable toolbar', () => { it('should reflect state in the UI', async () => { harness.togglePopover(); - expect(harness.fitRowToContentSwitch.prop('checked')).toBe(false); + expect(harness.rowHeight.prop('idSelected')).toBe('single'); expect(harness.paginationSwitch.prop('checked')).toBe(false); harness.wrapper.setProps({ state: { - fitRowToContent: true, + rowHeight: 'auto', paging: defaultPagingState, }, }); - expect(harness.fitRowToContentSwitch.prop('checked')).toBe(true); + expect(harness.rowHeight.prop('idSelected')).toBe('auto'); expect(harness.paginationSwitch.prop('checked')).toBe(true); }); - it('should toggle fit-row-to-content', async () => { + it('should change row height to "Auto" mode', async () => { harness.togglePopover(); - harness.toggleFitRowToContent(); + harness.changeRowHeight('auto'); expect(defaultProps.setState).toHaveBeenCalledTimes(1); expect(defaultProps.setState).toHaveBeenNthCalledWith(1, { - fitRowToContent: true, + rowHeight: 'auto', + rowHeightLines: undefined, }); - harness.wrapper.setProps({ state: { fitRowToContent: true } }); // update state manually - harness.toggleFitRowToContent(); // turn it off + harness.wrapper.setProps({ state: { rowHeight: 'auto' } }); // update state manually + harness.changeRowHeight('single'); // turn it off expect(defaultProps.setState).toHaveBeenCalledTimes(2); expect(defaultProps.setState).toHaveBeenNthCalledWith(2, { - fitRowToContent: false, + rowHeight: 'single', + rowHeightLines: 1, + }); + }); + + it('should change row height to "Custom" mode', async () => { + harness.togglePopover(); + + harness.changeRowHeight('custom'); + + expect(defaultProps.setState).toHaveBeenCalledTimes(1); + expect(defaultProps.setState).toHaveBeenNthCalledWith(1, { + rowHeight: 'custom', + rowHeightLines: 2, }); + + harness.wrapper.setProps({ state: { rowHeight: 'custom' } }); // update state manually + + expect(harness.rowHeightLines.prop('value')).toBe(2); }); it('should toggle table pagination', async () => { @@ -107,18 +151,18 @@ describe('datatable toolbar', () => { expect(defaultProps.setState).toHaveBeenCalledTimes(1); expect(defaultProps.setState).toHaveBeenNthCalledWith(1, { paging: defaultPagingState, - fitRowToContent: false, + rowHeight: 'single', }); // update state manually harness.wrapper.setProps({ - state: { fitRowToContent: false, paging: defaultPagingState }, + state: { rowHeight: 'single', paging: defaultPagingState }, }); harness.togglePagination(); // turn it off. this should disable pagination but preserve the default page size expect(defaultProps.setState).toHaveBeenCalledTimes(2); expect(defaultProps.setState).toHaveBeenNthCalledWith(2, { - fitRowToContent: false, + rowHeight: 'single', paging: { ...defaultPagingState, enabled: false }, }); }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/toolbar.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/toolbar.tsx index e95ae98e37563..548af6917c18d 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/toolbar.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/toolbar.tsx @@ -7,22 +7,47 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFlexGroup, EuiFormRow, EuiSwitch, EuiToolTip } from '@elastic/eui'; +import { + EuiButtonGroup, + EuiFlexGroup, + EuiFormRow, + EuiRange, + EuiSwitch, + EuiToolTip, + htmlIdGenerator, +} from '@elastic/eui'; import { ToolbarPopover } from '../../shared_components'; import type { VisualizationToolbarProps } from '../../types'; import type { DatatableVisualizationState } from '../visualization'; import { DEFAULT_PAGE_SIZE } from './table_basic'; +const idPrefix = htmlIdGenerator()(); + export function DataTableToolbar(props: VisualizationToolbarProps) { const { state, setState } = props; - const onToggleFitRow = useCallback(() => { - const current = state.fitRowToContent ?? false; - setState({ - ...state, - fitRowToContent: !current, - }); - }, [setState, state]); + const onChangeRowHeight = useCallback( + (newHeightMode) => { + const rowHeightLines = + newHeightMode === 'single' ? 1 : newHeightMode !== 'auto' ? 2 : undefined; + setState({ + ...state, + rowHeight: newHeightMode, + rowHeightLines, + }); + }, + [setState, state] + ); + + const onChangeRowHeightLines = useCallback( + (newRowHeightLines) => { + setState({ + ...state, + rowHeightLines: newRowHeightLines, + }); + }, + [state, setState] + ); const onTogglePagination = useCallback(() => { const current = state.paging ?? { size: DEFAULT_PAGE_SIZE, enabled: false }; @@ -33,6 +58,30 @@ export function DataTableToolbar(props: VisualizationToolbarProps - { + const newMode = optionId.replace( + idPrefix, + '' + ) as DatatableVisualizationState['rowHeight']; + onChangeRowHeight(newMode); + }} /> + {state.rowHeight === 'custom' ? ( + + { + const lineCount = Number(e.currentTarget.value); + onChangeRowHeightLines(lineCount); + }} + data-test-subj="lens-table-row-height-lineCountNumber" + /> + + ) : null} { ).toEqual([20]); }); - it('sets fitRowToContent based on state', () => { + it('sets rowHeight "auto" fit based on state', () => { expect( getDatatableExpressionArgs({ ...defaultExpressionTableState }).fitRowToContent ).toEqual([false]); expect( - getDatatableExpressionArgs({ ...defaultExpressionTableState, fitRowToContent: false }) + getDatatableExpressionArgs({ ...defaultExpressionTableState, rowHeight: 'single' }) .fitRowToContent ).toEqual([false]); expect( - getDatatableExpressionArgs({ ...defaultExpressionTableState, fitRowToContent: true }) + getDatatableExpressionArgs({ ...defaultExpressionTableState, rowHeight: 'custom' }) + .fitRowToContent + ).toEqual([false]); + + expect( + getDatatableExpressionArgs({ ...defaultExpressionTableState, rowHeight: 'auto' }) .fitRowToContent ).toEqual([true]); }); + + it('sets rowHeightLines fit based on state', () => { + expect(getDatatableExpressionArgs({ ...defaultExpressionTableState }).rowHeightLines).toEqual( + [1] + ); + + expect( + getDatatableExpressionArgs({ ...defaultExpressionTableState, rowHeight: 'single' }) + .rowHeightLines + ).toEqual([1]); + + // should ignore lines value based on mode + expect( + getDatatableExpressionArgs({ + ...defaultExpressionTableState, + rowHeight: 'single', + rowHeightLines: 5, + }).rowHeightLines + ).toEqual([1]); + + expect( + getDatatableExpressionArgs({ + ...defaultExpressionTableState, + rowHeight: 'custom', + rowHeightLines: 5, + }).rowHeightLines + ).toEqual([5]); + + // should fallback to 2 for custom in case it's not set + expect( + getDatatableExpressionArgs({ + ...defaultExpressionTableState, + rowHeight: 'custom', + }).rowHeightLines + ).toEqual([2]); + }); }); describe('#getErrorMessages', () => { diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index 8cc3709cade0b..3e9f35f80b2b8 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -32,7 +32,8 @@ export interface DatatableVisualizationState { layerId: string; layerType: LayerType; sorting?: SortingState; - fitRowToContent?: boolean; + rowHeight?: 'auto' | 'single' | 'custom'; + rowHeightLines?: number; paging?: PagingState; } @@ -400,7 +401,10 @@ export const getDatatableVisualization = ({ }), sortingColumnId: [state.sorting?.columnId || ''], sortingDirection: [state.sorting?.direction || 'none'], - fitRowToContent: [state.fitRowToContent ?? false], + fitRowToContent: [state.rowHeight === 'auto'], + rowHeightLines: [ + !state.rowHeight || state.rowHeight === 'single' ? 1 : state.rowHeightLines ?? 2, + ], pageSize: state.paging?.enabled ? [state.paging.size] : [], }, }, diff --git a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts index 39d36a9f306a2..506a5e7688f9a 100644 --- a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts +++ b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts @@ -13,6 +13,7 @@ import { } from '../../../../../src/plugins/kibana_utils/common'; import { DOC_TYPE } from '../../common'; import { + commonEnhanceTableRowHeight, commonMakeReversePaletteAsCustom, commonRemoveTimezoneDateHistogramParam, commonRenameFilterReferences, @@ -26,8 +27,10 @@ import { CustomVisualizationMigrations, LensDocShape713, LensDocShape715, + LensDocShape810, LensDocShapePre712, VisState716, + VisState810, VisStatePre715, } from '../migrations/types'; import { extract, inject } from '../../common/embeddable_factory'; @@ -88,6 +91,14 @@ export const makeLensEmbeddableFactory = attributes: migratedLensState, } as unknown as SerializableRecord; }, + '8.2.0': (state) => { + const lensState = state as unknown as { attributes: LensDocShape810 }; + const migratedLensState = commonEnhanceTableRowHeight(lensState.attributes); + return { + ...lensState, + attributes: migratedLensState, + } as unknown as SerializableRecord; + }, }), getLensCustomVisualizationMigrations(customVisualizationMigrations) ), diff --git a/x-pack/plugins/lens/server/migrations/common_migrations.ts b/x-pack/plugins/lens/server/migrations/common_migrations.ts index 39eed3cbc2a35..12ceff5b1e846 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -23,6 +23,8 @@ import { VisStatePost715, VisStatePre715, VisState716, + VisState810, + VisState820, CustomVisualizationMigrations, LensDocShape810, } from './types'; @@ -192,6 +194,20 @@ export const commonRenameFilterReferences = (attributes: LensDocShape715): LensD return newAttributes as LensDocShape810; }; +export const commonEnhanceTableRowHeight = ( + attributes: LensDocShape810 +): LensDocShape810 => { + if (attributes.visualizationType !== 'lnsDatatable') { + return attributes; + } + const visState810 = attributes.state.visualization as VisState810; + const newAttributes = cloneDeep(attributes); + const vizState = newAttributes.state.visualization as VisState820; + vizState.rowHeight = visState810.fitRowToContent ? 'auto' : 'single'; + vizState.rowHeightLines = visState810.fitRowToContent ? 2 : 1; + return newAttributes; +}; + const getApplyCustomVisualizationMigrationToLens = (id: string, migration: MigrateFunction) => { return (savedObject: { attributes: LensDocShape }) => { if (savedObject.attributes.visualizationType !== id) return savedObject; diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts index 5cd63b2786fe4..a051c24742694 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts @@ -18,6 +18,8 @@ import { VisState716, VisStatePost715, VisStatePre715, + VisState810, + VisState820, } from './types'; import { CustomPaletteParams, layerTypes } from '../../common'; import { PaletteOutput } from 'src/plugins/charts/common'; @@ -1779,4 +1781,122 @@ describe('Lens migrations', () => { ); }); }); + + describe('8.2.0 rename fitRowToContent to new detailed rowHeight and rowHeightLines', () => { + const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; + function getExample(fitToContent: boolean) { + return { + type: 'lens', + id: 'mocked-saved-object-id', + attributes: { + visualizationType: 'lnsDatatable', + title: 'Lens visualization', + references: [ + { + id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + name: 'indexpattern-datasource-current-indexpattern', + type: 'index-pattern', + }, + { + id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + name: 'indexpattern-datasource-layer-cddd8f79-fb20-4191-a3e7-92484780cc62', + type: 'index-pattern', + }, + ], + state: { + datasourceStates: { + indexpattern: { + layers: { + 'cddd8f79-fb20-4191-a3e7-92484780cc62': { + indexPatternId: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + columns: { + '221f0abf-6e54-4c61-9316-4107ad6fa500': { + label: 'Top values of category.keyword', + dataType: 'string', + operationType: 'terms', + scale: 'ordinal', + sourceField: 'category.keyword', + isBucketed: true, + params: { + size: 5, + orderBy: { + type: 'column', + columnId: 'c6f07a26-64eb-4871-ad62-c7d937230e33', + }, + orderDirection: 'desc', + otherBucket: true, + missingBucket: false, + parentFormat: { + id: 'terms', + }, + }, + }, + 'c6f07a26-64eb-4871-ad62-c7d937230e33': { + label: 'Count of records', + dataType: 'number', + operationType: 'count', + isBucketed: false, + scale: 'ratio', + sourceField: '___records___', + }, + }, + columnOrder: [ + '221f0abf-6e54-4c61-9316-4107ad6fa500', + 'c6f07a26-64eb-4871-ad62-c7d937230e33', + ], + incompleteColumns: {}, + }, + }, + }, + }, + visualization: { + columns: [ + { + isTransposed: false, + columnId: '221f0abf-6e54-4c61-9316-4107ad6fa500', + }, + { + isTransposed: false, + columnId: 'c6f07a26-64eb-4871-ad62-c7d937230e33', + }, + ], + layerId: 'cddd8f79-fb20-4191-a3e7-92484780cc62', + layerType: 'data', + fitRowToContent: fitToContent, + }, + filters: [], + query: { + query: '', + language: 'kuery', + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc; + } + + it('should migrate enabled fitRowToContent to new rowHeight: "auto"', () => { + const result = migrations['8.2.0'](getExample(true), context) as ReturnType< + SavedObjectMigrationFn, LensDocShape810> + >; + + expect(result.attributes.state.visualization as VisState820).toEqual( + expect.objectContaining({ + rowHeight: 'auto', + }) + ); + }); + + it('should migrate disabled fitRowToContent to new rowHeight: "single"', () => { + const result = migrations['8.2.0'](getExample(false), context) as ReturnType< + SavedObjectMigrationFn, LensDocShape810> + >; + + expect(result.attributes.state.visualization as VisState820).toEqual( + expect.objectContaining({ + rowHeight: 'single', + rowHeightLines: 1, + }) + ); + }); + }); }); diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts index 2617fb42bce09..00c490b4509f9 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -40,6 +40,7 @@ import { getLensCustomVisualizationMigrations, commonRenameRecordsField, fixLensTopValuesCustomFormatting, + commonEnhanceTableRowHeight, } from './common_migrations'; interface LensDocShapePre710 { @@ -464,6 +465,11 @@ const addParentFormatter: SavedObjectMigrationFn = (doc) => { + const newDoc = cloneDeep(doc); + return { ...newDoc, attributes: commonEnhanceTableRowHeight(newDoc.attributes) }; +}; + const lensMigrations: SavedObjectMigrationMap = { '7.7.0': removeInvalidAccessors, // The order of these migrations matter, since the timefield migration relies on the aggConfigs @@ -478,6 +484,7 @@ const lensMigrations: SavedObjectMigrationMap = { '7.15.0': addLayerTypeToVisualization, '7.16.0': moveDefaultReversedPaletteToCustom, '8.1.0': flow(renameFilterReferences, renameRecordsField, addParentFormatter), + '8.2.0': enhanceTableRowHeight, }; export const getAllMigrations = ( diff --git a/x-pack/plugins/lens/server/migrations/types.ts b/x-pack/plugins/lens/server/migrations/types.ts index 7cbb2052dbfff..b3f76d05acfdf 100644 --- a/x-pack/plugins/lens/server/migrations/types.ts +++ b/x-pack/plugins/lens/server/migrations/types.ts @@ -246,3 +246,14 @@ export type VisState716 = | { palette?: PaletteOutput; }; + +// Datatable only +export interface VisState810 { + fitRowToContent?: boolean; +} + +// Datatable only +export interface VisState820 { + rowHeight: 'auto' | 'single' | 'custom'; + rowHeightLines: number; +} diff --git a/x-pack/test/api_integration/apis/maps/migrations.js b/x-pack/test/api_integration/apis/maps/migrations.js index a5199ad987ca0..5ea24b5d25fc9 100644 --- a/x-pack/test/api_integration/apis/maps/migrations.js +++ b/x-pack/test/api_integration/apis/maps/migrations.js @@ -77,7 +77,7 @@ export default function ({ getService }) { } expect(panels.length).to.be(1); expect(panels[0].type).to.be('map'); - expect(panels[0].version).to.be('8.1.0'); + expect(panels[0].version).to.be('8.2.0'); }); }); }); From 5217b9ab617af1d5bd167eed53364eff350a0557 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 9 Mar 2022 09:09:52 -0600 Subject: [PATCH 119/140] skip failing test (#127076) --- .../server/integration_tests/cloud_preconfiguration.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts b/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts index 2dbdb58497506..02815347b712b 100644 --- a/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts @@ -18,7 +18,8 @@ import { CLOUD_KIBANA_CONFIG } from './fixtures/cloud_kibana_config'; const logFilePath = Path.join(__dirname, 'logs.log'); -describe('Fleet preconfiguration reset', () => { +// FAILING: https://github.com/elastic/kibana/issues/127076 +describe.skip('Fleet preconfiguration reset', () => { let esServer: kbnTestServer.TestElasticsearchUtils; let kbnServer: kbnTestServer.TestKibanaUtils; From d2c9a62e15335f191e2ac2a8f445e59df01c348e Mon Sep 17 00:00:00 2001 From: "Lucas F. da Costa" Date: Wed, 9 Mar 2022 15:51:39 +0000 Subject: [PATCH 120/140] Enable local mtls (#127116) * [Uptime] register synthetics service locations on startup * [Uptime] Enable mTLS through a devUrl for the synthetics service * [Uptime] deprecate basic auth for synthetics service * [Uptime] move the registration of the synthetics service location to the service constructor * re-enable basic authentication for environments other than production * only return monitor errors if the error array is not empty * fix: add missing username prop to ServiceAPIClient * use UUID for the monitors in tests to prevent name conflicts * adjust tests * Update x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts Co-authored-by: Dominique Clarke * do not surface edit errors from the service * do not surface delete errors from the service Co-authored-by: Dominique Clarke Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Dominique Clarke --- x-pack/plugins/uptime/e2e/config.ts | 6 +++++- .../journeys/monitor_management.journey.ts | 11 +++++----- .../action_bar/action_bar.tsx | 4 ++-- .../action_bar/action_bar_errors.test.tsx | 12 +++++++---- .../public/state/api/monitor_management.ts | 2 +- .../get_service_locations.ts | 14 ++++++++++++- .../synthetics_service/service_api_client.ts | 21 ++++++++++--------- .../synthetics_service/synthetics_service.ts | 16 +++++++++----- .../synthetics_service/add_monitor.ts | 8 ++++--- .../synthetics_service/delete_monitor.ts | 6 ++++-- .../synthetics_service/edit_monitor.ts | 6 ++++-- 11 files changed, 70 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/uptime/e2e/config.ts b/x-pack/plugins/uptime/e2e/config.ts index 48b4e048000f2..08cc1d960d044 100644 --- a/x-pack/plugins/uptime/e2e/config.ts +++ b/x-pack/plugins/uptime/e2e/config.ts @@ -57,7 +57,11 @@ async function config({ readConfigFile }: FtrConfigProviderContext) { `--elasticsearch.password=changeme`, '--xpack.reporting.enabled=false', `--xpack.uptime.service.manifestUrl=${manifestUrl}`, - `--xpack.uptime.service.username=${serviceUsername}`, + `--xpack.uptime.service.username=${ + process.env.SYNTHETICS_REMOTE_ENABLED + ? serviceUsername + : 'localKibanaIntegrationTestsUser' + }`, `--xpack.uptime.service.password=${servicPassword}`, '--xpack.uptime.ui.monitorManagement.enabled=true', ], diff --git a/x-pack/plugins/uptime/e2e/journeys/monitor_management.journey.ts b/x-pack/plugins/uptime/e2e/journeys/monitor_management.journey.ts index d89c9da043e4f..1d6270c00df65 100644 --- a/x-pack/plugins/uptime/e2e/journeys/monitor_management.journey.ts +++ b/x-pack/plugins/uptime/e2e/journeys/monitor_management.journey.ts @@ -5,6 +5,7 @@ * 2.0. */ +import uuid from 'uuid'; import { journey, step, expect, before, after, Page } from '@elastic/synthetics'; import { monitorManagementPageProvider } from '../page_objects/monitor_management'; import { DataStream } from '../../common/runtime_types/monitor_management'; @@ -16,10 +17,10 @@ const basicMonitorDetails = { location: customLocation || 'US Central', schedule: '3', }; -const httpName = 'http monitor'; -const icmpName = 'icmp monitor'; -const tcpName = 'tcp monitor'; -const browserName = 'browser monitor'; +const httpName = `http monitor ${uuid.v4()}`; +const icmpName = `icmp monitor ${uuid.v4()}`; +const tcpName = `tcp monitor ${uuid.v4()}`; +const browserName = `browser monitor ${uuid.v4()}`; const configuration = { [DataStream.HTTP]: { @@ -156,7 +157,7 @@ Object.keys(configuration).forEach((type) => { journey('Monitor Management breadcrumbs', async ({ page, params }: { page: Page; params: any }) => { const uptime = monitorManagementPageProvider({ page, kibanaUrl: params.kibanaUrl }); const defaultMonitorDetails = { - name: 'Sample monitor', + name: `Sample monitor ${uuid.v4()}`, location: 'US Central', schedule: '3', apmServiceName: 'service', diff --git a/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.tsx b/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.tsx index f54031766be8e..8a2e669129114 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar.tsx @@ -69,7 +69,7 @@ export const ActionBar = ({ }); }, [monitor, monitorId, isValid, isSaving]); - const hasErrors = data && Object.keys(data).length; + const hasErrors = data && 'attributes' in data && data.attributes.errors?.length > 0; const loading = status === FETCH_STATUS.LOADING; const handleOnSave = useCallback(() => { @@ -103,7 +103,7 @@ export const ActionBar = ({ }); setIsSuccessful(true); } else if (hasErrors && !loading) { - Object.values(data!).forEach((location) => { + Object.values(data.attributes.errors!).forEach((location) => { const { status: responseStatus, reason } = location.error || {}; kibanaService.toasts.addWarning({ title: i18n.translate('xpack.uptime.monitorManagement.service.error.title', { diff --git a/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar_errors.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar_errors.test.tsx index 1a882e5027313..88520b8761ae7 100644 --- a/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar_errors.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor_management/action_bar/action_bar_errors.test.tsx @@ -52,10 +52,14 @@ describe(' Service Errors', () => { it('Handles service errors', async () => { jest.spyOn(kibana.kibanaService.toasts, 'addWarning').mockImplementation(toast); useFetcher.mockReturnValue({ - data: [ - { locationId: 'us_central', error: { reason: 'Invalid config', status: 400 } }, - { locationId: 'us_central', error: { reason: 'Cannot schedule', status: 500 } }, - ], + data: { + attributes: { + errors: [ + { locationId: 'us_central', error: { reason: 'Invalid config', status: 400 } }, + { locationId: 'us_central', error: { reason: 'Cannot schedule', status: 500 } }, + ], + }, + }, status: FETCH_STATUS.SUCCESS, refetch: () => {}, }); diff --git a/x-pack/plugins/uptime/public/state/api/monitor_management.ts b/x-pack/plugins/uptime/public/state/api/monitor_management.ts index 206ba07dc4c23..00a033ec51b7a 100644 --- a/x-pack/plugins/uptime/public/state/api/monitor_management.ts +++ b/x-pack/plugins/uptime/public/state/api/monitor_management.ts @@ -24,7 +24,7 @@ export const setMonitor = async ({ }: { monitor: SyntheticsMonitor; id?: string; -}): Promise => { +}): Promise<{ attributes: { errors: ServiceLocationErrors } } | SyntheticsMonitor> => { if (id) { return await apiService.put(`${API_URLS.SYNTHETICS_MONITORS}/${id}`, monitor); } else { diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts index e746dafdb55d5..f1af840aac72f 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_service_locations.ts @@ -8,13 +8,25 @@ import axios from 'axios'; import { ManifestLocation, + ServiceLocation, Locations, ServiceLocationsApiResponse, } from '../../../common/runtime_types'; import { UptimeServerSetup } from '../adapters/framework'; +export const getDevLocation = (devUrl: string): ServiceLocation => ({ + id: 'localhost', + label: 'Local Synthetics Service', + geo: { lat: 0, lon: 0 }, + url: devUrl, +}); + export async function getServiceLocations(server: UptimeServerSetup) { - const locations: Locations = []; + let locations: Locations = []; + + if (process.env.NODE_ENV !== 'production' && server.config.service?.devUrl) { + locations = [getDevLocation(server.config.service.devUrl)]; + } if (!server.config.service?.manifestUrl) { return { locations }; diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts index 1e82ef77e083b..14c5a2ebb5959 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts @@ -32,7 +32,6 @@ export interface ServiceData { export class ServiceAPIClient { private readonly username?: string; - private readonly devUrl?: string; private readonly authorization: string; public locations: ServiceLocations; private logger: Logger; @@ -41,9 +40,8 @@ export class ServiceAPIClient { constructor(logger: Logger, config: ServiceConfig, kibanaVersion: string) { this.config = config; - const { username, password, devUrl } = config; + const { username, password } = config; this.username = username; - this.devUrl = devUrl; this.kibanaVersion = kibanaVersion; if (username && password) { @@ -61,8 +59,10 @@ export class ServiceAPIClient { if (config.tls && config.tls.certificate && config.tls.key) { const tlsConfig = new SslConfig(config.tls); + const rejectUnauthorized = process.env.NODE_ENV === 'production'; + return new https.Agent({ - rejectUnauthorized: true, // (NOTE: this will disable client verification) + rejectUnauthorized, cert: tlsConfig.certificate, key: tlsConfig.key, }); @@ -102,13 +102,14 @@ export class ServiceAPIClient { return axios({ method, - url: (this.devUrl ?? url) + (runOnce ? '/run' : '/monitors'), + url: url + (runOnce ? '/run' : '/monitors'), data: { monitors: monitorsStreams, output, stack_version: this.kibanaVersion }, - headers: this.authorization - ? { - Authorization: this.authorization, - } - : undefined, + headers: + process.env.NODE_ENV !== 'production' && this.authorization + ? { + Authorization: this.authorization, + } + : undefined, httpsAgent: this.getHttpsAgent(), }); }; diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts index acf3c0df49164..11dcf1973b41c 100644 --- a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts @@ -64,6 +64,8 @@ export class SyntheticsService { this.esHosts = getEsHosts({ config: this.config, cloud: server.cloud }); this.locations = []; + + this.registerServiceLocations(); } public init() { @@ -103,6 +105,14 @@ export class SyntheticsService { } } + public registerServiceLocations() { + const service = this; + getServiceLocations(service.server).then((result) => { + service.locations = result.locations; + service.apiClient.locations = result.locations; + }); + } + public registerSyncTask(taskManager: TaskManagerSetupContract) { const service = this; @@ -121,11 +131,7 @@ export class SyntheticsService { const { state } = taskInstance; service.setupIndexTemplates(); - - getServiceLocations(service.server).then((result) => { - service.locations = result.locations; - service.apiClient.locations = result.locations; - }); + service.registerServiceLocations(); await service.pushConfigs(); diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts index 29c2210efd20a..ee3ef1fe6380c 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts @@ -54,10 +54,12 @@ export const addSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ formatTelemetryEvent({ monitor: newMonitor, errors, kibanaVersion: server.kibanaVersion }) ); - if (errors) { - return errors; + if (errors && errors.length > 0) { + return response.ok({ + body: { message: 'error pushing monitor to the service', attributes: { errors } }, + }); } - return newMonitor; + return response.ok({ body: newMonitor }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts index bbdb6818b3f6a..6d744a7a1a406 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/delete_monitor.ts @@ -46,8 +46,10 @@ export const deleteSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ formatTelemetryDeleteEvent(monitor, server.kibanaVersion, new Date().toISOString(), errors) ); - if (errors) { - return errors; + if (errors && errors.length > 0) { + return response.ok({ + body: { message: 'error pushing monitor to the service', attributes: { errors } }, + }); } return monitorId; diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts index d8a6ec85e770e..6170bdd72ac36 100644 --- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/edit_monitor.ts @@ -78,8 +78,10 @@ export const editSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ ); // Return service sync errors in OK response - if (errors) { - return errors; + if (errors && errors.length > 0) { + return response.ok({ + body: { message: 'error pushing monitor to the service', attributes: { errors } }, + }); } return editMonitor; From 488d9398bc4059dcecad16f03200136f664d0add Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 9 Mar 2022 10:09:26 -0600 Subject: [PATCH 121/140] [ci] Remove package testing jenkins source (#127300) --- .ci/package-testing/Jenkinsfile | 29 -------------------- test/scripts/jenkins_xpack_package_build.sh | 12 -------- test/scripts/jenkins_xpack_package_deb.sh | 26 ------------------ test/scripts/jenkins_xpack_package_docker.sh | 26 ------------------ test/scripts/jenkins_xpack_package_rpm.sh | 26 ------------------ 5 files changed, 119 deletions(-) delete mode 100644 .ci/package-testing/Jenkinsfile delete mode 100755 test/scripts/jenkins_xpack_package_build.sh delete mode 100755 test/scripts/jenkins_xpack_package_deb.sh delete mode 100755 test/scripts/jenkins_xpack_package_docker.sh delete mode 100755 test/scripts/jenkins_xpack_package_rpm.sh diff --git a/.ci/package-testing/Jenkinsfile b/.ci/package-testing/Jenkinsfile deleted file mode 100644 index fec7dc9ea4cdc..0000000000000 --- a/.ci/package-testing/Jenkinsfile +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/groovy -library 'kibana-pipeline-library' -kibanaLibrary.load() -kibanaPipeline(timeoutMinutes: 120) { - slackNotifications.onFailure { - ciStats.trackBuild { - workers.ci(ramDisk: false, name: "package-build", size: 'l', runErrorReporter: false) { - withGcpServiceAccount.fromVaultSecret('secret/kibana-issues/dev/ci-artifacts-key', 'value') { - kibanaPipeline.bash("test/scripts/jenkins_xpack_package_build.sh", "Package builds") - } - } - def packageTypes = ['deb', 'docker', 'rpm'] - def workers = [:] - packageTypes.each { type -> - workers["package-${type}"] = { - testPackage(type) - } - } - parallel(workers) - } - } -} -def testPackage(packageType) { - workers.ci(ramDisk: false, name: "package-${packageType}", size: 's', runErrorReporter: false) { - withGcpServiceAccount.fromVaultSecret('secret/kibana-issues/dev/ci-artifacts-key', 'value') { - kibanaPipeline.bash("test/scripts/jenkins_xpack_package_${packageType}.sh", "Execute package testing for ${packageType}") - } - } -} diff --git a/test/scripts/jenkins_xpack_package_build.sh b/test/scripts/jenkins_xpack_package_build.sh deleted file mode 100755 index 86e846f720803..0000000000000 --- a/test/scripts/jenkins_xpack_package_build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e - -source src/dev/ci_setup/setup_env.sh - -export TMP=/tmp -export TMPDIR=/tmp - -node scripts/build --all-platforms --debug - -gsutil -q -m cp 'target/*' "gs://ci-artifacts.kibana.dev/package-testing/$GIT_COMMIT/" diff --git a/test/scripts/jenkins_xpack_package_deb.sh b/test/scripts/jenkins_xpack_package_deb.sh deleted file mode 100755 index 626036e8db3f5..0000000000000 --- a/test/scripts/jenkins_xpack_package_deb.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -set -e - -source src/dev/ci_setup/setup_env.sh - -mkdir -p target -gsutil -q -m cp "gs://ci-artifacts.kibana.dev/package-testing/$GIT_COMMIT/kibana-*.deb" ./target - -export VAGRANT_CWD=test/package -vagrant up deb --no-provision - -node scripts/es snapshot \ - -E network.bind_host=127.0.0.1,192.168.50.1 \ - -E discovery.type=single-node \ - --license=trial & -while ! timeout 1 bash -c "echo > /dev/tcp/localhost/9200"; do sleep 30; done - -vagrant provision deb - -export TEST_BROWSER_HEADLESS=1 -export TEST_KIBANA_URL=http://elastic:changeme@192.168.50.5:5601 -export TEST_ES_URL=http://elastic:changeme@192.168.50.1:9200 - -cd x-pack -node scripts/functional_test_runner.js --include-tag=smoke diff --git a/test/scripts/jenkins_xpack_package_docker.sh b/test/scripts/jenkins_xpack_package_docker.sh deleted file mode 100755 index c9f94b2c1eb4f..0000000000000 --- a/test/scripts/jenkins_xpack_package_docker.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -set -e - -source src/dev/ci_setup/setup_env.sh - -mkdir -p target -gsutil -q -m cp "gs://ci-artifacts.kibana.dev/package-testing/$GIT_COMMIT/kibana-[0-9]*-docker-image.tar.gz" ./target - -export VAGRANT_CWD=test/package -vagrant up docker --no-provision - -node scripts/es snapshot \ - -E network.bind_host=127.0.0.1,192.168.50.1 \ - -E discovery.type=single-node \ - --license=trial & -while ! timeout 1 bash -c "echo > /dev/tcp/localhost/9200"; do sleep 30; done - -vagrant provision docker - -export TEST_BROWSER_HEADLESS=1 -export TEST_KIBANA_URL=http://elastic:changeme@192.168.50.7:5601 -export TEST_ES_URL=http://elastic:changeme@192.168.50.1:9200 - -cd x-pack -node scripts/functional_test_runner.js --include-tag=smoke diff --git a/test/scripts/jenkins_xpack_package_rpm.sh b/test/scripts/jenkins_xpack_package_rpm.sh deleted file mode 100755 index 08095ce48c1e5..0000000000000 --- a/test/scripts/jenkins_xpack_package_rpm.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -set -e - -source src/dev/ci_setup/setup_env.sh - -mkdir -p target -gsutil -q -m cp "gs://ci-artifacts.kibana.dev/package-testing/$GIT_COMMIT/kibana-*.rpm" ./target - -export VAGRANT_CWD=test/package -vagrant up rpm --no-provision - -node scripts/es snapshot \ - -E network.bind_host=127.0.0.1,192.168.50.1 \ - -E discovery.type=single-node \ - --license=trial & -while ! timeout 1 bash -c "echo > /dev/tcp/localhost/9200"; do sleep 30; done - -vagrant provision rpm - -export TEST_BROWSER_HEADLESS=1 -export TEST_KIBANA_URL=http://elastic:changeme@192.168.50.6:5601 -export TEST_ES_URL=http://elastic:changeme@192.168.50.1:9200 - -cd x-pack -node scripts/functional_test_runner.js --include-tag=smoke From abc734f7a5b47274fd99acba7327140150056546 Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Wed, 9 Mar 2022 16:33:28 +0000 Subject: [PATCH 122/140] [APM] Fix tooltip focus on service map (#127138) * [APM] Fix tooltip focus on service map * remove commented code * put back EuiIconTip and add initialFocus to false on EuiPopover --- .../apm/public/components/app/service_map/popover/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/apm/public/components/app/service_map/popover/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/popover/index.tsx index d3fabd9a045fc..937ad89293a7c 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/popover/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/popover/index.tsx @@ -169,6 +169,7 @@ export function Popover({ isOpen={isOpen} ref={popoverRef} style={popoverStyle} + initialFocus={false} > Date: Wed, 9 Mar 2022 17:36:37 +0100 Subject: [PATCH 123/140] ci: discard configuration step and parallelism for performance test pipeline (#127288) - Removes user input and env var for performance test iterations - Changes performance test execution queue - Removes parallelism, queue mechanisms - Removes dynamic creation of performance sub-steps Co-authored-by: Spencer --- .buildkite/pipelines/performance/daily.yml | 11 +--- .../functional/performance_playwright.sh | 65 ++++++++++++++----- .../functional/performance_sub_playwright.sh | 55 ---------------- 3 files changed, 49 insertions(+), 82 deletions(-) delete mode 100644 .buildkite/scripts/steps/functional/performance_sub_playwright.sh diff --git a/.buildkite/pipelines/performance/daily.yml b/.buildkite/pipelines/performance/daily.yml index 564bfb5e501b3..658ab3a72f186 100644 --- a/.buildkite/pipelines/performance/daily.yml +++ b/.buildkite/pipelines/performance/daily.yml @@ -1,13 +1,4 @@ steps: - - block: ':gear: Performance Tests Configuration' - prompt: 'Fill out the details for performance test' - fields: - - text: ':arrows_counterclockwise: Iterations' - key: 'performance-test-iteration-count' - hint: 'How many times you want to run tests? ' - required: true - if: build.env('PERF_TEST_COUNT') == null - - label: ':male-mechanic::skin-tone-2: Pre-Build' command: .buildkite/scripts/lifecycle/pre_build.sh agents: @@ -24,7 +15,7 @@ steps: - label: ':muscle: Performance Tests with Playwright config' command: .buildkite/scripts/steps/functional/performance_playwright.sh agents: - queue: c2-16 + queue: kb-static-ubuntu depends_on: build - wait: ~ diff --git a/.buildkite/scripts/steps/functional/performance_playwright.sh b/.buildkite/scripts/steps/functional/performance_playwright.sh index c38ef5e56dbe4..596304d156cf0 100644 --- a/.buildkite/scripts/steps/functional/performance_playwright.sh +++ b/.buildkite/scripts/steps/functional/performance_playwright.sh @@ -1,24 +1,55 @@ -#!/bin/bash +#!/usr/bin/env bash -set -uo pipefail +set -euo pipefail -if [ -z "${PERF_TEST_COUNT+x}" ]; then - TEST_COUNT="$(buildkite-agent meta-data get performance-test-iteration-count)" -else - TEST_COUNT=$PERF_TEST_COUNT -fi +source .buildkite/scripts/common/util.sh -tput setab 2; tput setaf 0; echo "Performance test will be run at ${BUILDKITE_BRANCH} ${TEST_COUNT} times" +.buildkite/scripts/bootstrap.sh +.buildkite/scripts/download_build_artifacts.sh -cat << EOF | buildkite-agent pipeline upload -steps: - - command: .buildkite/scripts/steps/functional/performance_sub_playwright.sh - parallelism: "$TEST_COUNT" - concurrency: 20 - concurrency_group: 'performance-test-group' - agents: - queue: c2-16 -EOF +echo --- Run Performance Tests with Playwright config +node scripts/es snapshot& +esPid=$! +export TEST_ES_URL=http://elastic:changeme@localhost:9200 +export TEST_ES_DISABLE_STARTUP=true + +sleep 120 + +cd "$XPACK_DIR" + +jobId=$(npx uuid) +export TEST_JOB_ID="$jobId" + +journeys=("ecommerce_dashboard" "flight_dashboard" "web_logs_dashboard" "promotion_tracking_dashboard") + +for i in "${journeys[@]}"; do + echo "JOURNEY[${i}] is running" + + export TEST_PERFORMANCE_PHASE=WARMUP + export ELASTIC_APM_ACTIVE=false + export JOURNEY_NAME="${i}" + + checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Journey:${i},Phase: WARMUP)" \ + node scripts/functional_tests \ + --config test/performance/config.playwright.ts \ + --include "test/performance/tests/playwright/${i}.ts" \ + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --debug \ + --bail + + export TEST_PERFORMANCE_PHASE=TEST + export ELASTIC_APM_ACTIVE=true + + checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Journey:${i},Phase: TEST)" \ + node scripts/functional_tests \ + --config test/performance/config.playwright.ts \ + --include "test/performance/tests/playwright/${i}.ts" \ + --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ + --debug \ + --bail +done + +kill "$esPid" diff --git a/.buildkite/scripts/steps/functional/performance_sub_playwright.sh b/.buildkite/scripts/steps/functional/performance_sub_playwright.sh deleted file mode 100644 index 596304d156cf0..0000000000000 --- a/.buildkite/scripts/steps/functional/performance_sub_playwright.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source .buildkite/scripts/common/util.sh - -.buildkite/scripts/bootstrap.sh -.buildkite/scripts/download_build_artifacts.sh - -echo --- Run Performance Tests with Playwright config - -node scripts/es snapshot& - -esPid=$! - -export TEST_ES_URL=http://elastic:changeme@localhost:9200 -export TEST_ES_DISABLE_STARTUP=true - -sleep 120 - -cd "$XPACK_DIR" - -jobId=$(npx uuid) -export TEST_JOB_ID="$jobId" - -journeys=("ecommerce_dashboard" "flight_dashboard" "web_logs_dashboard" "promotion_tracking_dashboard") - -for i in "${journeys[@]}"; do - echo "JOURNEY[${i}] is running" - - export TEST_PERFORMANCE_PHASE=WARMUP - export ELASTIC_APM_ACTIVE=false - export JOURNEY_NAME="${i}" - - checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Journey:${i},Phase: WARMUP)" \ - node scripts/functional_tests \ - --config test/performance/config.playwright.ts \ - --include "test/performance/tests/playwright/${i}.ts" \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --debug \ - --bail - - export TEST_PERFORMANCE_PHASE=TEST - export ELASTIC_APM_ACTIVE=true - - checks-reporter-with-killswitch "Run Performance Tests with Playwright Config (Journey:${i},Phase: TEST)" \ - node scripts/functional_tests \ - --config test/performance/config.playwright.ts \ - --include "test/performance/tests/playwright/${i}.ts" \ - --kibana-install-dir "$KIBANA_BUILD_LOCATION" \ - --debug \ - --bail -done - -kill "$esPid" From 6280ca130b2428683cb5982776426d0634913c9d Mon Sep 17 00:00:00 2001 From: Ashokaditya <1849116+ashokaditya@users.noreply.github.com> Date: Wed, 9 Mar 2022 17:52:31 +0100 Subject: [PATCH 124/140] [Security Solution][Endpoint] Create endpoint action and responses from matching fleet actions (#127174) * Create endpoint action and responses from matching fleet actions Correctly generates fake endpoint action/responses data to match created actions on fleet actions index * add endpoint action and responses without a flag --- .../data_generators/fleet_action_generator.ts | 2 +- .../data_loaders/index_endpoint_actions.ts | 209 ----------- .../index_endpoint_fleet_actions.ts | 343 ++++++++++++++++++ .../data_loaders/index_endpoint_hosts.ts | 36 +- .../data_loaders/index_fleet_actions.ts | 219 ----------- .../common/endpoint/index_data.ts | 2 - .../endpoint/resolver_generator_script.ts | 9 - .../services/endpoint.ts | 7 +- 8 files changed, 355 insertions(+), 472 deletions(-) delete mode 100644 x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_actions.ts create mode 100644 x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_fleet_actions.ts delete mode 100644 x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts index 22d81ba4a3458..abe29f62dfd5b 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts @@ -57,7 +57,7 @@ export class FleetActionGenerator extends BaseDataGenerator { agent_id: this.randomUUID(), started_at: this.randomPastDate(), completed_at: timeStamp.toISOString(), - error: 'some error happen', + error: 'some error happened', '@timestamp': timeStamp.toISOString(), }, overrides diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_actions.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_actions.ts deleted file mode 100644 index 3c8d23e375159..0000000000000 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_actions.ts +++ /dev/null @@ -1,209 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Client } from '@elastic/elasticsearch'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { HostMetadata, LogsEndpointAction, LogsEndpointActionResponse } from '../types'; -import { EndpointActionGenerator } from '../data_generators/endpoint_action_generator'; -import { wrapErrorAndRejectPromise } from './utils'; -import { ENDPOINT_ACTIONS_INDEX, ENDPOINT_ACTION_RESPONSES_INDEX } from '../constants'; - -const defaultEndpointActionGenerator = new EndpointActionGenerator(); - -export interface IndexedEndpointActionsForHostResponse { - endpointActions: LogsEndpointAction[]; - endpointActionResponses: LogsEndpointActionResponse[]; - endpointActionsIndex: string; - endpointActionResponsesIndex: string; -} - -/** - * Indexes a random number of Endpoint Actions for a given host - * - * @param esClient - * @param endpointHost - * @param [endpointActionGenerator] - */ -export const indexEndpointActionsForHost = async ( - esClient: Client, - endpointHost: HostMetadata, - endpointActionGenerator: EndpointActionGenerator = defaultEndpointActionGenerator -): Promise => { - const agentId = endpointHost.elastic.agent.id; - const total = endpointActionGenerator.randomN(5); - const response: IndexedEndpointActionsForHostResponse = { - endpointActions: [], - endpointActionResponses: [], - endpointActionsIndex: ENDPOINT_ACTIONS_INDEX, - endpointActionResponsesIndex: ENDPOINT_ACTION_RESPONSES_INDEX, - }; - - for (let i = 0; i < total; i++) { - // create an action - const action = endpointActionGenerator.generate({ - EndpointActions: { - data: { comment: 'data generator: this host is same as bad' }, - }, - }); - - action.agent.id = [agentId]; - - await esClient - .index({ - index: ENDPOINT_ACTIONS_INDEX, - body: action, - }) - .catch(wrapErrorAndRejectPromise); - - // Create an action response for the above - const actionResponse = endpointActionGenerator.generateResponse({ - agent: { id: agentId }, - EndpointActions: { - action_id: action.EndpointActions.action_id, - data: action.EndpointActions.data, - }, - }); - - await esClient - .index({ - index: ENDPOINT_ACTION_RESPONSES_INDEX, - body: actionResponse, - }) - .catch(wrapErrorAndRejectPromise); - - response.endpointActions.push(action); - response.endpointActionResponses.push(actionResponse); - } - - // Add edge cases (maybe) - if (endpointActionGenerator.randomFloat() < 0.3) { - const randomFloat = endpointActionGenerator.randomFloat(); - - // 60% of the time just add either an Isolate -OR- an UnIsolate action - if (randomFloat < 0.6) { - let action: LogsEndpointAction; - - if (randomFloat < 0.3) { - // add a pending isolation - action = endpointActionGenerator.generateIsolateAction({ - '@timestamp': new Date().toISOString(), - }); - } else { - // add a pending UN-isolation - action = endpointActionGenerator.generateUnIsolateAction({ - '@timestamp': new Date().toISOString(), - }); - } - - action.agent.id = [agentId]; - - await esClient - .index({ - index: ENDPOINT_ACTIONS_INDEX, - body: action, - }) - .catch(wrapErrorAndRejectPromise); - - response.endpointActions.push(action); - } else { - // Else (40% of the time) add a pending isolate AND pending un-isolate - const action1 = endpointActionGenerator.generateIsolateAction({ - '@timestamp': new Date().toISOString(), - }); - const action2 = endpointActionGenerator.generateUnIsolateAction({ - '@timestamp': new Date().toISOString(), - }); - - action1.agent.id = [agentId]; - action2.agent.id = [agentId]; - - await Promise.all([ - esClient - .index({ - index: ENDPOINT_ACTIONS_INDEX, - body: action1, - }) - .catch(wrapErrorAndRejectPromise), - esClient - .index({ - index: ENDPOINT_ACTIONS_INDEX, - body: action2, - }) - .catch(wrapErrorAndRejectPromise), - ]); - - response.endpointActions.push(action1, action2); - } - } - - return response; -}; - -export interface DeleteIndexedEndpointActionsResponse { - endpointActionRequests: estypes.DeleteByQueryResponse | undefined; - endpointActionResponses: estypes.DeleteByQueryResponse | undefined; -} - -export const deleteIndexedEndpointActions = async ( - esClient: Client, - indexedData: IndexedEndpointActionsForHostResponse -): Promise => { - const response: DeleteIndexedEndpointActionsResponse = { - endpointActionRequests: undefined, - endpointActionResponses: undefined, - }; - - if (indexedData.endpointActions.length) { - response.endpointActionRequests = await esClient - .deleteByQuery({ - index: `${indexedData.endpointActionsIndex}-*`, - wait_for_completion: true, - body: { - query: { - bool: { - filter: [ - { - terms: { - action_id: indexedData.endpointActions.map( - (action) => action.EndpointActions.action_id - ), - }, - }, - ], - }, - }, - }, - }) - .catch(wrapErrorAndRejectPromise); - } - - if (indexedData.endpointActionResponses) { - response.endpointActionResponses = await esClient - .deleteByQuery({ - index: `${indexedData.endpointActionResponsesIndex}-*`, - wait_for_completion: true, - body: { - query: { - bool: { - filter: [ - { - terms: { - action_id: indexedData.endpointActionResponses.map( - (action) => action.EndpointActions.action_id - ), - }, - }, - ], - }, - }, - }, - }) - .catch(wrapErrorAndRejectPromise); - } - - return response; -}; diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_fleet_actions.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_fleet_actions.ts new file mode 100644 index 0000000000000..dc0f2b29ec94b --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_fleet_actions.ts @@ -0,0 +1,343 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Client } from '@elastic/elasticsearch'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { + EndpointAction, + EndpointActionResponse, + HostMetadata, + LogsEndpointAction, + LogsEndpointActionResponse, +} from '../types'; +import { ENDPOINT_ACTIONS_INDEX, ENDPOINT_ACTION_RESPONSES_INDEX } from '../constants'; +import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '../../../../fleet/common'; +import { FleetActionGenerator } from '../data_generators/fleet_action_generator'; +import { wrapErrorAndRejectPromise } from './utils'; + +const defaultFleetActionGenerator = new FleetActionGenerator(); + +export interface IndexedEndpointAndFleetActionsForHostResponse { + actions: EndpointAction[]; + actionResponses: EndpointActionResponse[]; + actionsIndex: string; + responsesIndex: string; + endpointActions: LogsEndpointAction[]; + endpointActionResponses: LogsEndpointActionResponse[]; + endpointActionsIndex: string; + endpointActionResponsesIndex: string; +} + +/** + * Indexes a random number of Endpoint (via Fleet) Actions for a given host + * (NOTE: ensure that fleet is setup first before calling this loading function) + * + * @param esClient + * @param endpointHost + * @param [fleetActionGenerator] + */ +export const indexEndpointAndFleetActionsForHost = async ( + esClient: Client, + endpointHost: HostMetadata, + fleetActionGenerator: FleetActionGenerator = defaultFleetActionGenerator +): Promise => { + const ES_INDEX_OPTIONS = { headers: { 'X-elastic-product-origin': 'fleet' } }; + const agentId = endpointHost.elastic.agent.id; + const total = fleetActionGenerator.randomN(5); + const response: IndexedEndpointAndFleetActionsForHostResponse = { + actions: [], + actionResponses: [], + endpointActions: [], + endpointActionResponses: [], + actionsIndex: AGENT_ACTIONS_INDEX, + responsesIndex: AGENT_ACTIONS_RESULTS_INDEX, + endpointActionsIndex: ENDPOINT_ACTIONS_INDEX, + endpointActionResponsesIndex: ENDPOINT_ACTION_RESPONSES_INDEX, + }; + + for (let i = 0; i < total; i++) { + // create an action + const action = fleetActionGenerator.generate({ + data: { comment: 'data generator: this host is bad' }, + }); + + action.agents = [agentId]; + const indexFleetActions = esClient + .index( + { + index: AGENT_ACTIONS_INDEX, + body: action, + }, + ES_INDEX_OPTIONS + ) + .catch(wrapErrorAndRejectPromise); + + if (fleetActionGenerator.randomFloat() < 0.4) { + const endpointActionsBody = { + EndpointActions: { + ...action, + '@timestamp': undefined, + user_id: undefined, + }, + agent: { + id: [agentId], + }, + '@timestamp': action['@timestamp'], + user: { + id: action.user_id, + }, + }; + + await Promise.all([ + indexFleetActions, + esClient + .index({ + index: ENDPOINT_ACTIONS_INDEX, + body: endpointActionsBody, + }) + .catch(wrapErrorAndRejectPromise), + ]); + } else { + await indexFleetActions; + } + + const randomFloat = fleetActionGenerator.randomFloat(); + // Create an action response for the above + const actionResponse = fleetActionGenerator.generateResponse({ + action_id: action.action_id, + agent_id: agentId, + action_response: { + endpoint: { + // add ack to 2/5th of fleet response + ack: randomFloat < 0.4 ? true : undefined, + }, + }, + // error for 3/10th of responses + error: randomFloat < 0.3 ? 'some error happened' : undefined, + }); + + const indexFleetResponses = esClient + .index( + { + index: AGENT_ACTIONS_RESULTS_INDEX, + body: actionResponse, + }, + ES_INDEX_OPTIONS + ) + .catch(wrapErrorAndRejectPromise); + + if (randomFloat < 0.4) { + const endpointActionResponseBody = { + EndpointActions: { + ...actionResponse, + data: actionResponse.action_data, + '@timestamp': undefined, + action_data: undefined, + agent_id: undefined, + error: undefined, + }, + agent: { + id: agentId, + }, + // error for 3/10th of responses + error: + randomFloat < 0.3 + ? undefined + : { + message: actionResponse.error, + }, + '@timestamp': actionResponse['@timestamp'], + }; + + await Promise.all([ + indexFleetResponses, + esClient + .index({ + index: ENDPOINT_ACTION_RESPONSES_INDEX, + body: endpointActionResponseBody, + }) + .catch(wrapErrorAndRejectPromise), + ]); + } else { + await indexFleetResponses; + } + + response.actions.push(action); + response.actionResponses.push(actionResponse); + } + + // Add edge cases (maybe) + if (fleetActionGenerator.randomFloat() < 0.3) { + const randomFloat = fleetActionGenerator.randomFloat(); + + // 60% of the time just add either an Isolate -OR- an UnIsolate action + if (randomFloat < 0.6) { + let action: EndpointAction; + + if (randomFloat < 0.3) { + // add a pending isolation + action = fleetActionGenerator.generateIsolateAction({ + '@timestamp': new Date().toISOString(), + }); + } else { + // add a pending UN-isolation + action = fleetActionGenerator.generateUnIsolateAction({ + '@timestamp': new Date().toISOString(), + }); + } + + action.agents = [agentId]; + + await esClient + .index( + { + index: AGENT_ACTIONS_INDEX, + body: action, + }, + ES_INDEX_OPTIONS + ) + .catch(wrapErrorAndRejectPromise); + + response.actions.push(action); + } else { + // Else (40% of the time) add a pending isolate AND pending un-isolate + const action1 = fleetActionGenerator.generateIsolateAction({ + '@timestamp': new Date().toISOString(), + }); + const action2 = fleetActionGenerator.generateUnIsolateAction({ + '@timestamp': new Date().toISOString(), + }); + + action1.agents = [agentId]; + action2.agents = [agentId]; + + await Promise.all([ + esClient + .index( + { + index: AGENT_ACTIONS_INDEX, + body: action1, + }, + ES_INDEX_OPTIONS + ) + .catch(wrapErrorAndRejectPromise), + esClient + .index( + { + index: AGENT_ACTIONS_INDEX, + body: action2, + }, + ES_INDEX_OPTIONS + ) + .catch(wrapErrorAndRejectPromise), + ]); + + response.actions.push(action1, action2); + } + } + + return response; +}; + +export interface DeleteIndexedEndpointFleetActionsResponse { + actions: estypes.DeleteByQueryResponse | undefined; + responses: estypes.DeleteByQueryResponse | undefined; + endpointActionRequests: estypes.DeleteByQueryResponse | undefined; + endpointActionResponses: estypes.DeleteByQueryResponse | undefined; +} + +export const deleteIndexedEndpointAndFleetActions = async ( + esClient: Client, + indexedData: IndexedEndpointAndFleetActionsForHostResponse +): Promise => { + const response: DeleteIndexedEndpointFleetActionsResponse = { + actions: undefined, + responses: undefined, + endpointActionRequests: undefined, + endpointActionResponses: undefined, + }; + + if (indexedData.actions.length) { + [response.actions, response.endpointActionRequests] = await Promise.all([ + esClient + .deleteByQuery({ + index: `${indexedData.actionsIndex}-*`, + wait_for_completion: true, + body: { + query: { + bool: { + filter: [ + { terms: { action_id: indexedData.actions.map((action) => action.action_id) } }, + ], + }, + }, + }, + }) + .catch(wrapErrorAndRejectPromise), + esClient + .deleteByQuery({ + index: `${indexedData.endpointActionsIndex}-*`, + wait_for_completion: true, + body: { + query: { + bool: { + filter: [ + { terms: { action_id: indexedData.actions.map((action) => action.action_id) } }, + ], + }, + }, + }, + }) + .catch(wrapErrorAndRejectPromise), + ]); + } + + if (indexedData.actionResponses) { + [response.responses, response.endpointActionResponses] = await Promise.all([ + esClient + .deleteByQuery({ + index: `${indexedData.responsesIndex}-*`, + wait_for_completion: true, + body: { + query: { + bool: { + filter: [ + { + terms: { + action_id: indexedData.actionResponses.map((action) => action.action_id), + }, + }, + ], + }, + }, + }, + }) + .catch(wrapErrorAndRejectPromise), + esClient + .deleteByQuery({ + index: `${indexedData.endpointActionResponsesIndex}-*`, + wait_for_completion: true, + body: { + query: { + bool: { + filter: [ + { + terms: { + action_id: indexedData.actionResponses.map((action) => action.action_id), + }, + }, + ], + }, + }, + }, + }) + .catch(wrapErrorAndRejectPromise), + ]); + } + + return response; +}; diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts index be26f8496c5e9..d2c42a1b40094 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts @@ -22,17 +22,11 @@ import { indexFleetAgentForHost, } from './index_fleet_agent'; import { - deleteIndexedFleetActions, - DeleteIndexedFleetActionsResponse, - IndexedFleetActionsForHostResponse, - indexFleetActionsForHost, -} from './index_fleet_actions'; -import { - deleteIndexedEndpointActions, - DeleteIndexedEndpointActionsResponse, - IndexedEndpointActionsForHostResponse, - indexEndpointActionsForHost, -} from './index_endpoint_actions'; + deleteIndexedEndpointAndFleetActions, + DeleteIndexedEndpointFleetActionsResponse, + IndexedEndpointAndFleetActionsForHostResponse, + indexEndpointAndFleetActionsForHost, +} from './index_endpoint_fleet_actions'; import { deleteIndexedFleetEndpointPolicies, @@ -45,8 +39,7 @@ import { EndpointDataLoadingError, wrapErrorAndRejectPromise } from './utils'; export interface IndexedHostsResponse extends IndexedFleetAgentResponse, - IndexedFleetActionsForHostResponse, - IndexedEndpointActionsForHostResponse, + IndexedEndpointAndFleetActionsForHostResponse, IndexedFleetEndpointPolicyResponse { /** * The documents (1 or more) that were generated for the (single) endpoint host. @@ -90,7 +83,6 @@ export async function indexEndpointHostDocs({ metadataIndex, policyResponseIndex, enrollFleet, - addEndpointActions, generator, }: { numDocs: number; @@ -101,7 +93,6 @@ export async function indexEndpointHostDocs({ metadataIndex: string; policyResponseIndex: string; enrollFleet: boolean; - addEndpointActions: boolean; generator: EndpointDocGenerator; }): Promise { const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents @@ -194,14 +185,7 @@ export async function indexEndpointHostDocs({ }; // Create some fleet endpoint actions and .logs-endpoint actions for this Host - if (addEndpointActions) { - await Promise.all([ - indexFleetActionsForHost(client, hostMetadata), - indexEndpointActionsForHost(client, hostMetadata), - ]); - } else { - await indexFleetActionsForHost(client, hostMetadata); - } + await indexEndpointAndFleetActionsForHost(client, hostMetadata, undefined); } hostMetadata = { @@ -259,8 +243,7 @@ const fetchKibanaVersion = async (kbnClient: KbnClient) => { export interface DeleteIndexedEndpointHostsResponse extends DeleteIndexedFleetAgentsResponse, - DeleteIndexedFleetActionsResponse, - DeleteIndexedEndpointActionsResponse, + DeleteIndexedEndpointFleetActionsResponse, DeleteIndexedFleetEndpointPoliciesResponse { hosts: DeleteByQueryResponse | undefined; policyResponses: DeleteByQueryResponse | undefined; @@ -335,8 +318,7 @@ export const deleteIndexedEndpointHosts = async ( } merge(response, await deleteIndexedFleetAgents(esClient, indexedData)); - merge(response, await deleteIndexedFleetActions(esClient, indexedData)); - merge(response, await deleteIndexedEndpointActions(esClient, indexedData)); + merge(response, await deleteIndexedEndpointAndFleetActions(esClient, indexedData)); merge(response, await deleteIndexedFleetEndpointPolicies(kbnClient, indexedData)); return response; diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts deleted file mode 100644 index 47448be2e0a92..0000000000000 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts +++ /dev/null @@ -1,219 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Client } from '@elastic/elasticsearch'; -import { DeleteByQueryResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { EndpointAction, EndpointActionResponse, HostMetadata } from '../types'; -import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '../../../../fleet/common'; -import { FleetActionGenerator } from '../data_generators/fleet_action_generator'; -import { wrapErrorAndRejectPromise } from './utils'; - -const defaultFleetActionGenerator = new FleetActionGenerator(); - -export interface IndexedFleetActionsForHostResponse { - actions: EndpointAction[]; - actionResponses: EndpointActionResponse[]; - actionsIndex: string; - responsesIndex: string; -} - -/** - * Indexes a randome number of Endpoint (via Fleet) Actions for a given host - * (NOTE: ensure that fleet is setup first before calling this loading function) - * - * @param esClient - * @param endpointHost - * @param [fleetActionGenerator] - */ -export const indexFleetActionsForHost = async ( - esClient: Client, - endpointHost: HostMetadata, - fleetActionGenerator: FleetActionGenerator = defaultFleetActionGenerator -): Promise => { - const ES_INDEX_OPTIONS = { headers: { 'X-elastic-product-origin': 'fleet' } }; - const agentId = endpointHost.elastic.agent.id; - const total = fleetActionGenerator.randomN(5); - const response: IndexedFleetActionsForHostResponse = { - actions: [], - actionResponses: [], - actionsIndex: AGENT_ACTIONS_INDEX, - responsesIndex: AGENT_ACTIONS_RESULTS_INDEX, - }; - - for (let i = 0; i < total; i++) { - // create an action - const action = fleetActionGenerator.generate({ - data: { comment: 'data generator: this host is bad' }, - }); - - action.agents = [agentId]; - - esClient - .index( - { - index: AGENT_ACTIONS_INDEX, - body: action, - }, - ES_INDEX_OPTIONS - ) - .catch(wrapErrorAndRejectPromise); - - // Create an action response for the above - const actionResponse = fleetActionGenerator.generateResponse({ - action_id: action.action_id, - agent_id: agentId, - action_response: { - endpoint: { - // add ack to 4/5th of fleet response - ack: fleetActionGenerator.randomFloat() < 0.8 ? true : undefined, - }, - }, - }); - - esClient - .index( - { - index: AGENT_ACTIONS_RESULTS_INDEX, - body: actionResponse, - }, - ES_INDEX_OPTIONS - ) - .catch(wrapErrorAndRejectPromise); - - response.actions.push(action); - response.actionResponses.push(actionResponse); - } - - // Add edge cases (maybe) - if (fleetActionGenerator.randomFloat() < 0.3) { - const randomFloat = fleetActionGenerator.randomFloat(); - - // 60% of the time just add either an Isolate -OR- an UnIsolate action - if (randomFloat < 0.6) { - let action: EndpointAction; - - if (randomFloat < 0.3) { - // add a pending isolation - action = fleetActionGenerator.generateIsolateAction({ - '@timestamp': new Date().toISOString(), - }); - } else { - // add a pending UN-isolation - action = fleetActionGenerator.generateUnIsolateAction({ - '@timestamp': new Date().toISOString(), - }); - } - - action.agents = [agentId]; - - await esClient - .index( - { - index: AGENT_ACTIONS_INDEX, - body: action, - }, - ES_INDEX_OPTIONS - ) - .catch(wrapErrorAndRejectPromise); - - response.actions.push(action); - } else { - // Else (40% of the time) add a pending isolate AND pending un-isolate - const action1 = fleetActionGenerator.generateIsolateAction({ - '@timestamp': new Date().toISOString(), - }); - const action2 = fleetActionGenerator.generateUnIsolateAction({ - '@timestamp': new Date().toISOString(), - }); - - action1.agents = [agentId]; - action2.agents = [agentId]; - - await Promise.all([ - esClient - .index( - { - index: AGENT_ACTIONS_INDEX, - body: action1, - }, - ES_INDEX_OPTIONS - ) - .catch(wrapErrorAndRejectPromise), - esClient - .index( - { - index: AGENT_ACTIONS_INDEX, - body: action2, - }, - ES_INDEX_OPTIONS - ) - .catch(wrapErrorAndRejectPromise), - ]); - - response.actions.push(action1, action2); - } - } - - return response; -}; - -export interface DeleteIndexedFleetActionsResponse { - actions: DeleteByQueryResponse | undefined; - responses: DeleteByQueryResponse | undefined; -} - -export const deleteIndexedFleetActions = async ( - esClient: Client, - indexedData: IndexedFleetActionsForHostResponse -): Promise => { - const response: DeleteIndexedFleetActionsResponse = { - actions: undefined, - responses: undefined, - }; - - if (indexedData.actions.length) { - response.actions = await esClient - .deleteByQuery({ - index: `${indexedData.actionsIndex}-*`, - wait_for_completion: true, - body: { - query: { - bool: { - filter: [ - { terms: { action_id: indexedData.actions.map((action) => action.action_id) } }, - ], - }, - }, - }, - }) - .catch(wrapErrorAndRejectPromise); - } - - if (indexedData.actionResponses) { - response.responses = await esClient - .deleteByQuery({ - index: `${indexedData.responsesIndex}-*`, - wait_for_completion: true, - body: { - query: { - bool: { - filter: [ - { - terms: { - action_id: indexedData.actionResponses.map((action) => action.action_id), - }, - }, - ], - }, - }, - }, - }) - .catch(wrapErrorAndRejectPromise); - } - - return response; -}; diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index 5c81196a3709c..57c20ffa242ea 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -57,7 +57,6 @@ export async function indexHostsAndAlerts( alertIndex: string, alertsPerHost: number, fleet: boolean, - logsEndpoint: boolean, options: TreeOptions = {} ): Promise { const random = seedrandom(seed); @@ -103,7 +102,6 @@ export async function indexHostsAndAlerts( metadataIndex, policyResponseIndex, enrollFleet: fleet, - addEndpointActions: logsEndpoint, generator, }); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts index 7181b97b4ff68..1d0b07f1b64e1 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts @@ -232,14 +232,6 @@ async function main() { type: 'boolean', default: false, }, - logsEndpoint: { - alias: 'le', - describe: - 'By default .logs-endpoint.action and .logs-endpoint.action.responses are not indexed. \ - Add endpoint actions and responses using this option. Starting with v7.16.0.', - type: 'boolean', - default: false, - }, ssl: { alias: 'ssl', describe: 'Use https for elasticsearch and kbn clients', @@ -354,7 +346,6 @@ async function main() { argv.alertIndex, argv.alertsPerHost, argv.fleet, - argv.logsEndpoint, { ancestors: argv.ancestors, generations: argv.generations, diff --git a/x-pack/test/security_solution_endpoint/services/endpoint.ts b/x-pack/test/security_solution_endpoint/services/endpoint.ts index e2d3a77e46f61..2fd03ca51e346 100644 --- a/x-pack/test/security_solution_endpoint/services/endpoint.ts +++ b/x-pack/test/security_solution_endpoint/services/endpoint.ts @@ -85,7 +85,7 @@ export class EndpointTestResources extends FtrService { * @param [options.alertsPerHost=1] Number of Alerts and Events to be loaded per Endpoint Host * @param [options.enableFleetIntegration=true] When set to `true`, Fleet data will also be loaded (ex. Integration Policies, Agent Policies, "fake" Agents) * @param [options.generatorSeed='seed`] The seed to be used by the data generator. Important in order to ensure the same data is generated on very run. - * @param [options.waitUntilTransformed=true] If set to `true`, the data loading process will wait until the endpoint hosts metadata is processd by the transform + * @param [options.waitUntilTransformed=true] If set to `true`, the data loading process will wait until the endpoint hosts metadata is processed by the transform */ async loadEndpointData( options: Partial<{ @@ -93,7 +93,6 @@ export class EndpointTestResources extends FtrService { numHostDocs: number; alertsPerHost: number; enableFleetIntegration: boolean; - logsEndpoint: boolean; generatorSeed: string; waitUntilTransformed: boolean; }> = {} @@ -103,7 +102,6 @@ export class EndpointTestResources extends FtrService { numHostDocs = 1, alertsPerHost = 1, enableFleetIntegration = true, - logsEndpoint = false, generatorSeed = 'seed', waitUntilTransformed = true, } = options; @@ -120,8 +118,7 @@ export class EndpointTestResources extends FtrService { 'logs-endpoint.events.process-default', 'logs-endpoint.alerts-default', alertsPerHost, - enableFleetIntegration, - logsEndpoint + enableFleetIntegration ); if (waitUntilTransformed) { From f169fe73254cde014bff7f9389b29712d816cce7 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 9 Mar 2022 12:23:47 -0500 Subject: [PATCH 125/140] [Fleet] Use docker registry in fleet cloud preconfiguration test (#127296) --- .../integration_tests/cloud_preconfiguration.test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts b/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts index 02815347b712b..f3a4e045d042d 100644 --- a/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts @@ -18,8 +18,7 @@ import { CLOUD_KIBANA_CONFIG } from './fixtures/cloud_kibana_config'; const logFilePath = Path.join(__dirname, 'logs.log'); -// FAILING: https://github.com/elastic/kibana/issues/127076 -describe.skip('Fleet preconfiguration reset', () => { +describe('Fleet preconfiguration reset', () => { let esServer: kbnTestServer.TestElasticsearchUtils; let kbnServer: kbnTestServer.TestKibanaUtils; @@ -40,8 +39,13 @@ describe.skip('Fleet preconfiguration reset', () => { const startKibana = async () => { const root = kbnTestServer.createRootWithCorePlugins( { - ...CLOUD_KIBANA_CONFIG, - 'xpack.fleet.registryUrl': registryUrl, + xpack: { + ...CLOUD_KIBANA_CONFIG.xpack, + fleet: { + ...CLOUD_KIBANA_CONFIG.xpack.fleet, + registryUrl, + }, + }, logging: { appenders: { file: { From 5e8d66ea749a7b041ff0ba6a13f41c2efa071fe5 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 9 Mar 2022 11:26:46 -0600 Subject: [PATCH 126/140] Remove logs.tar.gz (#127299) This looks like it was accidentally committed in https://github.com/elastic/kibana/pull/125372 --- logs.tar.gz | Bin 8081 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 logs.tar.gz diff --git a/logs.tar.gz b/logs.tar.gz deleted file mode 100644 index 2a1acc4b5eba1e77ccd0c4a066511d261caff28b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8081 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 fwuHl@)9{ Date: Wed, 9 Mar 2022 09:28:10 -0800 Subject: [PATCH 127/140] [Fleet] Fix ES error during filtering on agent status (#127234) * Remove `body` key from expected ES response, get ES to return hits as integer only, remove expect error annotations * Apply treatment to other areas returning total counts * Lint * Fix tests --- x-pack/plugins/fleet/server/services/agent_policy.ts | 4 ++-- x-pack/plugins/fleet/server/services/agents/crud.test.ts | 2 +- x-pack/plugins/fleet/server/services/agents/crud.ts | 9 ++++++--- .../fleet/server/services/api_keys/enrollment_api_key.ts | 4 ++-- .../fleet/server/services/artifacts/artifacts.test.ts | 4 ++++ .../plugins/fleet/server/services/artifacts/artifacts.ts | 5 +++-- x-pack/plugins/fleet/server/services/artifacts/mocks.ts | 8 +++----- .../plugins/fleet/server/services/fleet_server/index.ts | 6 ++++-- 8 files changed, 25 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 1784ff190385d..6a191863fc2f7 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -667,6 +667,7 @@ class AgentPolicyService { const res = await esClient.search({ index: AGENT_POLICY_INDEX, ignore_unavailable: true, + rest_total_hits_as_int: true, body: { query: { term: { @@ -678,8 +679,7 @@ class AgentPolicyService { }, }); - // @ts-expect-error value is number | TotalHits - if (res.body.hits.total.value === 0) { + if ((res.hits.total as number) === 0) { return null; } diff --git a/x-pack/plugins/fleet/server/services/agents/crud.test.ts b/x-pack/plugins/fleet/server/services/agents/crud.test.ts index 6ae8fbd471238..f6988c1c7d280 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud.test.ts @@ -30,7 +30,7 @@ describe('Agents CRUD test', () => { function getEsResponse(ids: string[], total: number) { return { hits: { - total: { value: total }, + total, hits: ids.map((id: string) => ({ _id: id, _source: {}, diff --git a/x-pack/plugins/fleet/server/services/agents/crud.ts b/x-pack/plugins/fleet/server/services/agents/crud.ts index 426bddd1bd9b9..03b71ceb496f2 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud.ts @@ -128,6 +128,7 @@ export async function getAgentsByKuery( from, size, track_total_hits: true, + rest_total_hits_as_int: true, ignore_unavailable: true, body: { ...body, @@ -137,7 +138,7 @@ export async function getAgentsByKuery( const res = await queryAgents((page - 1) * perPage, perPage); let agents = res.hits.hits.map(searchHitToAgent); - let total = (res.hits.total as estypes.SearchTotalHits).value; + let total = res.hits.total as number; // filtering for a range on the version string will not work, // nor does filtering on a flattened field (local_metadata), so filter here if (showUpgradeable) { @@ -202,11 +203,13 @@ export async function countInactiveAgents( index: AGENTS_INDEX, size: 0, track_total_hits: true, + rest_total_hits_as_int: true, + filter_path: 'hits.total', ignore_unavailable: true, body, }); - // @ts-expect-error value is number | TotalHits - return res.body.hits.total.value; + + return (res.hits.total as number) || 0; } export async function getAgentById(esClient: ElasticsearchClient, agentId: string) { diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts index 03c9e4f979953..ee5d982811e31 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts @@ -43,6 +43,7 @@ export async function listEnrollmentApiKeys( from: (page - 1) * perPage, size: perPage, track_total_hits: true, + rest_total_hits_as_int: true, ignore_unavailable: true, body: { sort: [{ created_at: { order: 'desc' } }], @@ -55,8 +56,7 @@ export async function listEnrollmentApiKeys( return { items, - // @ts-expect-error value is number | TotalHits - total: res.hits.total.value, + total: res.hits.total as number, page, perPage, }; diff --git a/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts b/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts index fedb89c1e779b..3f0c5af247eb5 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/artifacts.test.ts @@ -148,6 +148,8 @@ describe('When using the artifacts services', () => { q: '', from: 0, size: 20, + track_total_hits: true, + rest_total_hits_as_int: true, body: { sort: [{ created: { order: 'asc' } }], }, @@ -182,6 +184,8 @@ describe('When using the artifacts services', () => { ignore_unavailable: true, from: 450, size: 50, + track_total_hits: true, + rest_total_hits_as_int: true, body: { sort: [{ identifier: { order: 'desc' } }], }, diff --git a/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts b/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts index 336a03e841266..c6e77301bed25 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/artifacts.ts @@ -107,6 +107,8 @@ export const listArtifacts = async ( from: (page - 1) * perPage, ignore_unavailable: true, size: perPage, + track_total_hits: true, + rest_total_hits_as_int: true, body: { sort: [{ [sortField]: { order: sortOrder } }], }, @@ -117,8 +119,7 @@ export const listArtifacts = async ( items: searchResult.hits.hits.map((hit) => esSearchHitToArtifact(hit)), page, perPage, - // @ts-expect-error doesn't handle total as number - total: searchResult.hits.total.value, + total: searchResult.hits.total as number, }; } catch (e) { throw new ArtifactsElasticsearchError(e); diff --git a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts index b122d3343dacd..9eee24afe08b2 100644 --- a/x-pack/plugins/fleet/server/services/artifacts/mocks.ts +++ b/x-pack/plugins/fleet/server/services/artifacts/mocks.ts @@ -102,7 +102,8 @@ export const generateArtifactEsGetSingleHitMock = ( export const generateArtifactEsSearchResultHitsMock = (): ESSearchResponse< ArtifactElasticsearchProperties, - {} + {}, + { restTotalHitsAsInt: true } > => { return { took: 0, @@ -114,10 +115,7 @@ export const generateArtifactEsSearchResultHitsMock = (): ESSearchResponse< failed: 0, }, hits: { - total: { - value: 1, - relation: 'eq', - }, + total: 1, max_score: 2, hits: [generateArtifactEsGetSingleHitMock()], }, diff --git a/x-pack/plugins/fleet/server/services/fleet_server/index.ts b/x-pack/plugins/fleet/server/services/fleet_server/index.ts index 0ac47df1bfdaf..43403bf3c9460 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server/index.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server/index.ts @@ -16,8 +16,10 @@ export async function hasFleetServers(esClient: ElasticsearchClient) { const res = await esClient.search<{}, {}>({ index: FLEET_SERVER_SERVERS_INDEX, ignore_unavailable: true, + filter_path: 'hits.total', + track_total_hits: true, + rest_total_hits_as_int: true, }); - // @ts-expect-error value is number | TotalHits - return res.hits.total.value > 0; + return (res.hits.total as number) > 0; } From aa82d757c150d655d0deb5113afdb44fb14eb331 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Wed, 9 Mar 2022 09:29:12 -0800 Subject: [PATCH 128/140] [Fleet] Remove dependency on `fieldFormatters` to fix Last activity and Size on data streams page (#127221) * Remove dependency on fieldFormatters, add `size_in_bytes_formatted` to be returned from data streams API (passthrough of formatted value from ES) * Add back seconds to activity time * Update openAPI spec * Remove references to fieldFormats * Fix test * Lint --- .../fleet/.storybook/context/stubs.tsx | 2 -- .../components/schemas/data_stream.yaml | 4 ++- .../fleet/common/types/models/data_stream.ts | 1 + .../sections/data_stream/list_page/index.tsx | 33 +++++++------------ .../agent_policy_selection.tsx | 2 +- .../fleet/public/mock/plugin_dependencies.ts | 2 -- x-pack/plugins/fleet/public/plugin.ts | 2 -- .../server/routes/data_streams/handlers.ts | 14 +++----- .../apis/data_streams/list.ts | 3 +- 9 files changed, 23 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/fleet/.storybook/context/stubs.tsx b/x-pack/plugins/fleet/.storybook/context/stubs.tsx index 65485a31d376a..0f4f81b58f95d 100644 --- a/x-pack/plugins/fleet/.storybook/context/stubs.tsx +++ b/x-pack/plugins/fleet/.storybook/context/stubs.tsx @@ -11,7 +11,6 @@ type Stubs = | 'licensing' | 'storage' | 'data' - | 'fieldFormats' | 'deprecations' | 'fatalErrors' | 'navigation' @@ -24,7 +23,6 @@ export const stubbedStartServices: StubbedStartServices = { licensing: {} as FleetStartServices['licensing'], storage: {} as FleetStartServices['storage'], data: {} as FleetStartServices['data'], - fieldFormats: {} as FleetStartServices['fieldFormats'], deprecations: {} as FleetStartServices['deprecations'], fatalErrors: {} as FleetStartServices['fatalErrors'], navigation: {} as FleetStartServices['navigation'], diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/data_stream.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/data_stream.yaml index 3d717ae910907..8cee31f95f845 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/data_stream.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/data_stream.yaml @@ -14,8 +14,10 @@ properties: package_version: type: string last_activity_ms: - type: string + type: number size_in_bytes: + type: number + size_in_bytes_formatted: type: string dashboard: type: array diff --git a/x-pack/plugins/fleet/common/types/models/data_stream.ts b/x-pack/plugins/fleet/common/types/models/data_stream.ts index 91c7069dfd5f2..7bf6d0f93dc55 100644 --- a/x-pack/plugins/fleet/common/types/models/data_stream.ts +++ b/x-pack/plugins/fleet/common/types/models/data_stream.ts @@ -14,6 +14,7 @@ export interface DataStream { package_version: string; last_activity_ms: number; size_in_bytes: number; + size_in_bytes_formatted: number | string; dashboards: Array<{ id: string; title: string; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/list_page/index.tsx index 29ea4102bc1ef..a4693407569ef 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/list_page/index.tsx @@ -16,10 +16,10 @@ import { EuiInMemoryTable, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage, FormattedDate } from '@kbn/i18n-react'; +import { FormattedMessage, FormattedDate, FormattedTime } from '@kbn/i18n-react'; import type { DataStream } from '../../../types'; -import { useGetDataStreams, useStartServices, usePagination, useBreadcrumbs } from '../../../hooks'; +import { useGetDataStreams, usePagination, useBreadcrumbs } from '../../../hooks'; import { PackageIcon } from '../../../components'; import { DataStreamRowActions } from './components/data_stream_row_actions'; @@ -27,8 +27,6 @@ import { DataStreamRowActions } from './components/data_stream_row_actions'; export const DataStreamListPage: React.FunctionComponent<{}> = () => { useBreadcrumbs('data_streams'); - const { fieldFormats } = useStartServices(); - const { pagination, pageSizeOptions } = usePagination(); // Fetch data streams @@ -97,30 +95,21 @@ export const DataStreamListPage: React.FunctionComponent<{}> = () => { defaultMessage: 'Last activity', }), render: (date: DataStream['last_activity_ms']) => { - try { - const formatter = fieldFormats.getInstance('date', { - pattern: 'MMM D, YYYY @ HH:mm:ss', - }); - return formatter.convert(date); - } catch (e) { - return ; - } + return ( + <> + + <> @ + + + ); }, }, { - field: 'size_in_bytes', + field: 'size_in_bytes_formatted', sortable: true, name: i18n.translate('xpack.fleet.dataStreamList.sizeColumnTitle', { defaultMessage: 'Size', }), - render: (size: DataStream['size_in_bytes']) => { - try { - const formatter = fieldFormats.getInstance('bytes'); - return formatter.convert(size); - } catch (e) { - return `${size}b`; - } - }, }, { name: i18n.translate('xpack.fleet.dataStreamList.actionsColumnTitle', { @@ -134,7 +123,7 @@ export const DataStreamListPage: React.FunctionComponent<{}> = () => { }, ]; return cols; - }, [fieldFormats]); + }, []); const emptyPrompt = useMemo( () => ( diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx index 8260692616106..3227308d93b9c 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx @@ -162,7 +162,7 @@ export const EnrollmentStepAgentPolicy: React.FC = (props) => { defaultMessage: 'Agent policy', } )} - hasNoInitialSelection={agentPolicies.length > 1} + hasNoInitialSelection={!selectedAgentPolicyId} data-test-subj="agentPolicyDropdown" isInvalid={!selectedAgentPolicyId} /> diff --git a/x-pack/plugins/fleet/public/mock/plugin_dependencies.ts b/x-pack/plugins/fleet/public/mock/plugin_dependencies.ts index 2545e63709765..842b690eb978f 100644 --- a/x-pack/plugins/fleet/public/mock/plugin_dependencies.ts +++ b/x-pack/plugins/fleet/public/mock/plugin_dependencies.ts @@ -12,7 +12,6 @@ import { homePluginMock } from '../../../../../src/plugins/home/public/mocks'; import { navigationPluginMock } from '../../../../../src/plugins/navigation/public/mocks'; import { customIntegrationsMock } from '../../../../../src/plugins/custom_integrations/public/mocks'; import { sharePluginMock } from '../../../../../src/plugins/share/public/mocks'; -import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; export const createSetupDepsMock = () => { const cloud = cloudMock.createSetup(); @@ -28,7 +27,6 @@ export const createStartDepsMock = () => { return { licensing: licensingMock.createStart(), data: dataPluginMock.createStartContract(), - fieldFormats: fieldFormatsServiceMock.createStartContract() as any, navigation: navigationPluginMock.createStartContract(), customIntegrations: customIntegrationsMock.createStart(), share: sharePluginMock.createStartContract(), diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index b8bda08177a7e..4848b05084676 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -38,7 +38,6 @@ import type { DataPublicPluginSetup, DataPublicPluginStart, } from '../../../../src/plugins/data/public'; -import type { FieldFormatsStart } from '../../../../src/plugins/field_formats/public/index'; import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import type { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { Storage } from '../../../../src/plugins/kibana_utils/public'; @@ -96,7 +95,6 @@ export interface FleetSetupDeps { export interface FleetStartDeps { licensing: LicensingPluginStart; data: DataPublicPluginStart; - fieldFormats: FieldFormatsStart; navigation: NavigationPublicPluginStart; customIntegrations: CustomIntegrationsStart; share: SharePluginStart; diff --git a/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts b/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts index 070def907bcff..437806abf4e9e 100644 --- a/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts @@ -37,13 +37,6 @@ interface ESDataStreamInfo { hidden: boolean; } -interface ESDataStreamStats { - data_stream: string; - backing_indices: number; - store_size_bytes: number; - maximum_timestamp: number; -} - export const getListHandler: RequestHandler = async (context, request, response) => { // Query datastreams as the current user as the Kibana internal user may not have all the required permission const esClient = context.core.elasticsearch.client.asCurrentUser; @@ -60,12 +53,12 @@ export const getListHandler: RequestHandler = async (context, request, response) packageSavedObjects, ] = await Promise.all([ esClient.indices.getDataStream({ name: DATA_STREAM_INDEX_PATTERN }), - esClient.indices.dataStreamsStats({ name: DATA_STREAM_INDEX_PATTERN }), + esClient.indices.dataStreamsStats({ name: DATA_STREAM_INDEX_PATTERN, human: true }), getPackageSavedObjects(context.core.savedObjects.client), ]); const dataStreamsInfoByName = keyBy(dataStreamsInfo, 'name'); - const dataStreamsStatsByName = keyBy(dataStreamStats, 'data_stream'); + const dataStreamsStatsByName = keyBy(dataStreamStats, 'data_stream'); // Combine data stream info const dataStreams = merge(dataStreamsInfoByName, dataStreamsStatsByName); @@ -127,6 +120,9 @@ export const getListHandler: RequestHandler = async (context, request, response) package_version: '', last_activity_ms: dataStream.maximum_timestamp, // overridden below if maxIngestedTimestamp agg returns a result size_in_bytes: dataStream.store_size_bytes, + // `store_size` should be available from ES due to ?human=true flag + // but fallback to bytes just in case + size_in_bytes_formatted: dataStream.store_size || `${dataStream.store_size_bytes}b`, dashboards: [], }; diff --git a/x-pack/test/fleet_api_integration/apis/data_streams/list.ts b/x-pack/test/fleet_api_integration/apis/data_streams/list.ts index 9a34b31bf421b..542abb97c1a16 100644 --- a/x-pack/test/fleet_api_integration/apis/data_streams/list.ts +++ b/x-pack/test/fleet_api_integration/apis/data_streams/list.ts @@ -145,7 +145,8 @@ export default function (providerContext: FtrProviderContext) { body.data_streams.forEach((dataStream: any) => { // eslint-disable-next-line @typescript-eslint/naming-convention - const { index, size_in_bytes, last_activity_ms, ...coreFields } = dataStream; + const { index, size_in_bytes, size_in_bytes_formatted, last_activity_ms, ...coreFields } = + dataStream; expect(expectedStreamsByDataset[coreFields.dataset]).not.to.eql(undefined); expect(coreFields).to.eql(expectedStreamsByDataset[coreFields.dataset]); }); From ab109db73fac793c1b854a64978baa7ff378854c Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 9 Mar 2022 11:47:11 -0600 Subject: [PATCH 129/140] [package testing] Update networking profile to account for virtualbox update (#127298) With a recent virtualbox update, we're getting failures in CI with: > The IP address configured for the host-only network is not within the > allowed ranges. Please update the address used to be within the allowed > ranges and run the command again. This updates the IP range used to be compatible. --- .buildkite/scripts/steps/package_testing/test.sh | 10 +++++----- .../contributing/development-package-tests.asciidoc | 10 +++++----- test/package/Vagrantfile | 6 +++--- .../package/roles/install_kibana_docker/tasks/main.yml | 2 +- test/package/templates/kibana.yml | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.buildkite/scripts/steps/package_testing/test.sh b/.buildkite/scripts/steps/package_testing/test.sh index 8fcb665b67a97..a9a46502d5b3b 100755 --- a/.buildkite/scripts/steps/package_testing/test.sh +++ b/.buildkite/scripts/steps/package_testing/test.sh @@ -10,13 +10,13 @@ mkdir -p target cd target if [[ "$TEST_PACKAGE" == "deb" ]]; then buildkite-agent artifact download 'kibana-*.deb' . --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" - KIBANA_IP_ADDRESS="192.168.50.5" + KIBANA_IP_ADDRESS="192.168.56.5" elif [[ "$TEST_PACKAGE" == "rpm" ]]; then buildkite-agent artifact download 'kibana-*.rpm' . --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" - KIBANA_IP_ADDRESS="192.168.50.6" + KIBANA_IP_ADDRESS="192.168.56.6" elif [[ "$TEST_PACKAGE" == "docker" ]]; then buildkite-agent artifact download "kibana-$KIBANA_PKG_VERSION-SNAPSHOT-docker-image.tar.gz" . --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" - KIBANA_IP_ADDRESS="192.168.50.7" + KIBANA_IP_ADDRESS="192.168.56.7" fi cd .. @@ -24,7 +24,7 @@ export VAGRANT_CWD=test/package vagrant up "$TEST_PACKAGE" --no-provision node scripts/es snapshot \ - -E network.bind_host=127.0.0.1,192.168.50.1 \ + -E network.bind_host=127.0.0.1,192.168.56.1 \ -E discovery.type=single-node \ --license=trial & while ! timeout 1 bash -c "echo > /dev/tcp/localhost/9200"; do sleep 30; done @@ -33,7 +33,7 @@ vagrant provision "$TEST_PACKAGE" export TEST_BROWSER_HEADLESS=1 export TEST_KIBANA_URL="http://elastic:changeme@$KIBANA_IP_ADDRESS:5601" -export TEST_ES_URL=http://elastic:changeme@192.168.50.1:9200 +export TEST_ES_URL=http://elastic:changeme@192.168.56.1:9200 cd x-pack node scripts/functional_test_runner.js --include-tag=smoke diff --git a/docs/developer/contributing/development-package-tests.asciidoc b/docs/developer/contributing/development-package-tests.asciidoc index 7883ce2d83209..2b43013992878 100644 --- a/docs/developer/contributing/development-package-tests.asciidoc +++ b/docs/developer/contributing/development-package-tests.asciidoc @@ -27,9 +27,9 @@ pip3 install --user ansible [cols=",,",options="header",] |=== |Hostname |IP |Description -|deb |192.168.50.5 |Installation of Kibana’s deb package -|rpm |192.168.50.6 |Installation of Kibana’s rpm package -|docker |192.168.50.7 |Installation of Kibana’s docker image +|deb |192.168.56.5 |Installation of Kibana’s deb package +|rpm |192.168.56.6 |Installation of Kibana’s rpm package +|docker |192.168.56.7 |Installation of Kibana’s docker image |=== === Running @@ -49,11 +49,11 @@ vagrant provision # Running functional tests node scripts/es snapshot \ - -E network.bind_host=127.0.0.1,192.168.50.1 \ + -E network.bind_host=127.0.0.1,192.168.56.1 \ -E discovery.type=single-node \ --license=trial TEST_KIBANA_URL=http://elastic:changeme@:5601 \ -TEST_ES_URL=http://elastic:changeme@192.168.50.1:9200 \ +TEST_ES_URL=http://elastic:changeme@192.168.56.1:9200 \ node scripts/functional_test_runner.js --include-tag=smoke ``` diff --git a/test/package/Vagrantfile b/test/package/Vagrantfile index 58fdd55734184..6a094e013c341 100644 --- a/test/package/Vagrantfile +++ b/test/package/Vagrantfile @@ -9,7 +9,7 @@ Vagrant.configure("2") do |config| deb.vm.provision "ansible" do |ansible| ansible.playbook = "deb.yml" end - deb.vm.network "private_network", ip: "192.168.50.5" + deb.vm.network "private_network", ip: "192.168.56.5" end config.vm.define "rpm" do |rpm| @@ -20,7 +20,7 @@ Vagrant.configure("2") do |config| rpm.vm.provision "ansible" do |ansible| ansible.playbook = "rpm.yml" end - rpm.vm.network "private_network", ip: "192.168.50.6" + rpm.vm.network "private_network", ip: "192.168.56.6" end config.vm.define "docker" do |docker| @@ -31,6 +31,6 @@ Vagrant.configure("2") do |config| docker.vm.provision "ansible" do |ansible| ansible.playbook = "docker.yml" end - docker.vm.network "private_network", ip: "192.168.50.7" + docker.vm.network "private_network", ip: "192.168.56.7" end end diff --git a/test/package/roles/install_kibana_docker/tasks/main.yml b/test/package/roles/install_kibana_docker/tasks/main.yml index c883a3ece2a14..2b0b70de30b6c 100644 --- a/test/package/roles/install_kibana_docker/tasks/main.yml +++ b/test/package/roles/install_kibana_docker/tasks/main.yml @@ -21,6 +21,6 @@ network_mode: host env: SERVER_HOST: 0.0.0.0 - ELASTICSEARCH_HOSTS: http://192.168.50.1:9200 + ELASTICSEARCH_HOSTS: http://192.168.56.1:9200 ELASTICSEARCH_USERNAME: '{{ elasticsearch_username }}' ELASTICSEARCH_PASSWORD: '{{ elasticsearch_password }}' diff --git a/test/package/templates/kibana.yml b/test/package/templates/kibana.yml index 710434a2a5775..16f5aee16f400 100644 --- a/test/package/templates/kibana.yml +++ b/test/package/templates/kibana.yml @@ -1,6 +1,6 @@ server.host: 0.0.0.0 -elasticsearch.hosts: http://192.168.50.1:9200 +elasticsearch.hosts: http://192.168.56.1:9200 elasticsearch.username: "{{ elasticsearch_username }}" elasticsearch.password: "{{ elasticsearch_password }}" From 0ba204bdc2dcf9c02fc2bbe1829957952c6c7777 Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Wed, 9 Mar 2022 18:50:02 +0100 Subject: [PATCH 130/140] [Workplace Search] Connect button now displays correct copy (#127291) * [Workplace Search] Connect button now displays correct copy --- .../add_source/configured_sources_list.tsx | 17 +++++++++++++++-- .../components/add_source/constants.ts | 7 ------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.tsx index d4bb62901cdb6..d4157caae2d1b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/configured_sources_list.tsx @@ -20,6 +20,8 @@ import { EuiToolTip, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + import { EuiButtonEmptyTo } from '../../../../../shared/react_router_helpers'; import { SourceIcon } from '../../../../components/shared/source_icon'; import { getAddPath, getSourcesPath } from '../../../../routes'; @@ -29,7 +31,6 @@ import { hasMultipleConnectorOptions } from '../../../../utils'; import { CONFIGURED_SOURCES_LIST_UNCONNECTED_TOOLTIP, CONFIGURED_SOURCES_LIST_ACCOUNT_ONLY_TOOLTIP, - CONFIGURED_SOURCES_CONNECT_BUTTON, CONFIGURED_SOURCES_EMPTY_STATE, CONFIGURED_SOURCES_TITLE, CONFIGURED_SOURCES_EMPTY_BODY, @@ -112,7 +113,19 @@ export const ConfiguredSourcesList: React.FC = ({ hasMultipleConnectorOptions(sourceData) ? '' : 'connect' }`} > - {CONFIGURED_SOURCES_CONNECT_BUTTON} + {!connected + ? i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.configuredSources.connectButton', + { + defaultMessage: 'Connect', + } + ) + : i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.contentSource.configuredSources.connectAnotherButton', + { + defaultMessage: 'Connect another', + } + )} )) || ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/constants.ts index 3ce4f930b7a38..5963f4cb25635 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/constants.ts @@ -223,13 +223,6 @@ export const CONFIGURED_SOURCES_LIST_ACCOUNT_ONLY_TOOLTIP = i18n.translate( } ); -export const CONFIGURED_SOURCES_CONNECT_BUTTON = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.contentSource.configuredSources.connectButton', - { - defaultMessage: 'Connect another', - } -); - export const CONFIGURED_SOURCES_EMPTY_STATE = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.contentSource.configuredSources.emptyState', { From 0bc7a2ee357db4190e0eeba13d1f87227074a41f Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Wed, 9 Mar 2022 12:14:51 -0600 Subject: [PATCH 131/140] [DOCS] Adds missing `monitoring.cluster_alerts.email_notifications.email_address` monitoring setting (#126826) * [DOCS] Adds missing deprecated monitoring settings * Review comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/settings/monitoring-settings.asciidoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index a328700ebeb5f..02beb39864b0f 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -29,10 +29,13 @@ For more information, see [[monitoring-general-settings]] ==== General monitoring settings +`monitoring.cluster_alerts.email_notifications.email_address` {ess-icon}:: +deprecated:[7.11.0,"In 8.2.0 and later, this setting will no longer be supported."] +When enabled, specifies the email address where you want to receive cluster alert notifications. + `monitoring.ui.ccs.enabled`:: Set to `true` (default) to enable {ref}/modules-cross-cluster-search.html[cross-cluster search] of your monitoring data. The {ref}/modules-remote-clusters.html#remote-cluster-settings[`remote_cluster_client`] role must exist on each node. - `monitoring.ui.elasticsearch.hosts`:: Specifies the location of the {es} cluster where your monitoring data is stored. + From e086d64f0af6a2f867db3651113b260bcef2e2a9 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 9 Mar 2022 18:22:01 +0000 Subject: [PATCH 132/140] [ML] Space aware trained models (#123487) * [ML] Space aware models * adding initialization * adding UI * fixing model list pagination * adding task manager service * updating type * moving log function * query optimisations * renaming models to trained models * adding trained model ui callout * translations * updating type registration test * fixing tests * saved object type rename * adding put trained model endpoint * fixing types * fixing esclient responses * fixing trained model endpoints * fixing endpoints using _all * fixing logic in trained models path * allow wildcards for get trained models * fixing dfa test * updating types * code clean up * updating route info * adding trained model capabilities * adding defer_definition_decompression * improving sync SO lists * changing sync response format * correcting model label in space selector * translations * fixing tests * fixing route capability * updating types * changes based on review * fixing sync items count * refreshing overview warning after sync * fixing types and tests * adding access to saved object types * reducing log level for pipeline warning * removing old todo comment Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Tyler Smalley --- .../type_registrations.test.ts | 1 + .../ml/common/constants/trained_models.ts | 8 + .../plugins/ml/common/types/capabilities.ts | 20 +- .../plugins/ml/common/types/saved_objects.ts | 30 +- .../plugins/ml/common/types/trained_models.ts | 4 +- x-pack/plugins/ml/kibana.json | 39 +- .../job_spaces_list/job_spaces_list.tsx | 46 +- .../job_spaces_sync_flyout.tsx | 22 +- .../components/job_spaces_sync/sync_list.tsx | 89 ++-- .../saved_objects_warning.tsx | 12 +- .../contexts/kibana/kibana_context.ts | 2 + .../components/analytics_list/use_columns.tsx | 2 +- .../components/jobs_list/jobs_list.js | 2 +- .../jobs_list_page/jobs_list_page.tsx | 49 +- .../application/management/jobs_list/index.ts | 4 + .../services/ml_api_service/saved_objects.ts | 35 +- .../services/ml_api_service/trained_models.ts | 11 +- .../models_management/models_list.tsx | 363 +++++++++----- .../capabilities/check_capabilities.test.ts | 40 +- .../plugins/ml/server/lib/ml_client/errors.ts | 10 + .../plugins/ml/server/lib/ml_client/index.ts | 2 +- .../ml/server/lib/ml_client/ml_client.ts | 90 +++- .../plugins/ml/server/lib/ml_client/types.ts | 8 + x-pack/plugins/ml/server/lib/route_guard.ts | 1 + .../data_frame_analytics/models_provider.ts | 1 - x-pack/plugins/ml/server/plugin.ts | 10 + x-pack/plugins/ml/server/routes/apidoc.json | 4 + .../plugins/ml/server/routes/saved_objects.ts | 128 ++++- .../server/routes/schemas/inference_schema.ts | 4 + .../ml/server/routes/schemas/saved_objects.ts | 8 +- .../ml/server/routes/trained_models.ts | 86 +++- .../ml/server/saved_objects/authorization.ts | 4 +- .../plugins/ml/server/saved_objects/checks.ts | 162 +++++- .../plugins/ml/server/saved_objects/index.ts | 1 + .../initialization/initialization.ts | 13 +- .../ml/server/saved_objects/mappings.ts | 28 ++ .../ml/server/saved_objects/saved_objects.ts | 14 +- .../ml/server/saved_objects/service.ts | 464 ++++++++++++++++-- .../plugins/ml/server/saved_objects/sync.ts | 218 ++++++-- .../ml/server/saved_objects/sync_task.ts | 141 ++++++ .../plugins/ml/server/saved_objects/util.ts | 21 +- .../server/shared_services/shared_services.ts | 20 +- x-pack/plugins/ml/server/types.ts | 3 + .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../apis/ml/saved_objects/initialize.ts | 2 +- .../apis/ml/saved_objects/sync.ts | 24 +- .../ml/saved_objects/update_jobs_spaces.ts | 4 +- .../apis/ml/system/capabilities.ts | 12 +- .../apis/ml/system/space_capabilities.ts | 22 +- x-pack/test/functional/services/ml/api.ts | 20 +- .../services/ml/stack_management_jobs.ts | 2 +- 52 files changed, 1886 insertions(+), 422 deletions(-) create mode 100644 x-pack/plugins/ml/server/saved_objects/sync_task.ts diff --git a/src/core/server/saved_objects/migrations/integration_tests/type_registrations.test.ts b/src/core/server/saved_objects/migrations/integration_tests/type_registrations.test.ts index 962d32b44eb32..969d9d64435b7 100644 --- a/src/core/server/saved_objects/migrations/integration_tests/type_registrations.test.ts +++ b/src/core/server/saved_objects/migrations/integration_tests/type_registrations.test.ts @@ -68,6 +68,7 @@ const previouslyRegisteredTypes = [ 'maps-telemetry', 'metrics-explorer-view', 'ml-job', + 'ml-trained-model', 'ml-module', 'ml-telemetry', 'monitoring-telemetry', diff --git a/x-pack/plugins/ml/common/constants/trained_models.ts b/x-pack/plugins/ml/common/constants/trained_models.ts index 019189ea13c05..e7508af45f5b6 100644 --- a/x-pack/plugins/ml/common/constants/trained_models.ts +++ b/x-pack/plugins/ml/common/constants/trained_models.ts @@ -12,3 +12,11 @@ export const DEPLOYMENT_STATE = { } as const; export type DeploymentState = typeof DEPLOYMENT_STATE[keyof typeof DEPLOYMENT_STATE]; + +export const TRAINED_MODEL_TYPE = { + PYTORCH: 'pytorch', + TREE_ENSEMBLE: 'tree_ensemble', + LANG_IDENT: 'lang_ident', +} as const; + +export type TrainedModelType = typeof TRAINED_MODEL_TYPE[keyof typeof TRAINED_MODEL_TYPE]; diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts index 36377aaa1ed3f..f99e99df5606f 100644 --- a/x-pack/plugins/ml/common/types/capabilities.ts +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -7,7 +7,11 @@ import { KibanaRequest } from 'kibana/server'; import { PLUGIN_ID } from '../constants/app'; -import { ML_SAVED_OBJECT_TYPE } from './saved_objects'; +import { + ML_JOB_SAVED_OBJECT_TYPE, + ML_MODULE_SAVED_OBJECT_TYPE, + ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, +} from './saved_objects'; import { ML_ALERT_TYPES } from '../constants/alerts'; export const apmUserMlCapabilities = { @@ -32,6 +36,8 @@ export const userMlCapabilities = { canDeleteAnnotation: false, // Alerts canUseMlAlerts: false, + // Trained models + canGetTrainedModels: false, }; export const adminMlCapabilities = { @@ -65,6 +71,10 @@ export const adminMlCapabilities = { canUseMlAlerts: false, // Model management canViewMlNodes: false, + // Trained models + canCreateTrainedModels: false, + canDeleteTrainedModels: false, + canStartStopTrainedModels: false, }; export type UserMlCapabilities = typeof userMlCapabilities; @@ -88,13 +98,15 @@ export function getPluginPrivileges() { const userMlCapabilitiesKeys = Object.keys(userMlCapabilities); const adminMlCapabilitiesKeys = Object.keys(adminMlCapabilities); const allMlCapabilitiesKeys = [...adminMlCapabilitiesKeys, ...userMlCapabilitiesKeys]; - // TODO: include ML in base privileges for the `8.0` release: https://github.com/elastic/kibana/issues/71422 + const savedObjects = [ 'index-pattern', 'dashboard', 'search', 'visualization', - ML_SAVED_OBJECT_TYPE, + ML_JOB_SAVED_OBJECT_TYPE, + ML_MODULE_SAVED_OBJECT_TYPE, + ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, ]; const privilege = { app: [PLUGIN_ID, 'kibana'], @@ -149,7 +161,7 @@ export function getPluginPrivileges() { catalogue: [], savedObject: { all: [], - read: [ML_SAVED_OBJECT_TYPE], + read: [ML_JOB_SAVED_OBJECT_TYPE], }, api: apmUserMlCapabilitiesKeys.map((k) => `ml:${k}`), ui: apmUserMlCapabilitiesKeys, diff --git a/x-pack/plugins/ml/common/types/saved_objects.ts b/x-pack/plugins/ml/common/types/saved_objects.ts index e376fddbe6272..b1f46240733cb 100644 --- a/x-pack/plugins/ml/common/types/saved_objects.ts +++ b/x-pack/plugins/ml/common/types/saved_objects.ts @@ -7,18 +7,31 @@ import type { ErrorType } from '../util/errors'; export type JobType = 'anomaly-detector' | 'data-frame-analytics'; -export const ML_SAVED_OBJECT_TYPE = 'ml-job'; +export type TrainedModelType = 'trained-model'; + +export const ML_JOB_SAVED_OBJECT_TYPE = 'ml-job'; +export const ML_TRAINED_MODEL_SAVED_OBJECT_TYPE = 'ml-trained-model'; export const ML_MODULE_SAVED_OBJECT_TYPE = 'ml-module'; +export type MlSavedObjectType = + | typeof ML_JOB_SAVED_OBJECT_TYPE + | typeof ML_TRAINED_MODEL_SAVED_OBJECT_TYPE + | typeof ML_MODULE_SAVED_OBJECT_TYPE; export interface SavedObjectResult { - [jobId: string]: { success: boolean; type: JobType; error?: ErrorType }; + [id: string]: { success: boolean; type: JobType | TrainedModelType; error?: ErrorType }; } +export type SyncResult = { + [jobType in JobType | TrainedModelType]?: { + [id: string]: { success: boolean; error?: ErrorType }; + }; +}; + export interface SyncSavedObjectResponse { - savedObjectsCreated: SavedObjectResult; - savedObjectsDeleted: SavedObjectResult; - datafeedsAdded: SavedObjectResult; - datafeedsRemoved: SavedObjectResult; + savedObjectsCreated: SyncResult; + savedObjectsDeleted: SyncResult; + datafeedsAdded: SyncResult; + datafeedsRemoved: SyncResult; } export interface CanDeleteJobResponse { @@ -32,9 +45,14 @@ export type JobsSpacesResponse = { [jobType in JobType]: { [jobId: string]: string[] }; }; +export interface TrainedModelsSpacesResponse { + trainedModels: { [id: string]: string[] }; +} + export interface InitializeSavedObjectResponse { jobs: Array<{ id: string; type: JobType }>; datafeeds: Array<{ id: string; type: JobType }>; + trainedModels: Array<{ id: string }>; success: boolean; error?: ErrorType; } diff --git a/x-pack/plugins/ml/common/types/trained_models.ts b/x-pack/plugins/ml/common/types/trained_models.ts index 7e4ed94c3ccab..d6eda37f99465 100644 --- a/x-pack/plugins/ml/common/types/trained_models.ts +++ b/x-pack/plugins/ml/common/types/trained_models.ts @@ -8,7 +8,7 @@ import type { DataFrameAnalyticsConfig } from './data_frame_analytics'; import type { FeatureImportanceBaseline, TotalFeatureImportance } from './feature_importance'; import type { XOR } from './common'; -import type { DeploymentState } from '../constants/trained_models'; +import type { DeploymentState, TrainedModelType } from '../constants/trained_models'; export interface IngestStats { count: number; @@ -103,7 +103,7 @@ export interface TrainedModelConfigResponse { model_aliases?: string[]; } & Record; model_id: string; - model_type: 'tree_ensemble' | 'pytorch' | 'lang_ident'; + model_type: TrainedModelType; tags: string[]; version: string; inference_config?: Record; diff --git a/x-pack/plugins/ml/kibana.json b/x-pack/plugins/ml/kibana.json index 54ef526fb511b..71a1b0b489c97 100644 --- a/x-pack/plugins/ml/kibana.json +++ b/x-pack/plugins/ml/kibana.json @@ -7,44 +7,45 @@ "ml" ], "requiredPlugins": [ + "cloud", "data", "dataViews", - "cloud", - "features", "dataVisualizer", + "discover", + "embeddable", + "features", + "fieldFormats", "licensing", "share", - "embeddable", - "uiActions", - "discover", + "taskManager", "triggersActionsUi", - "fieldFormats" + "uiActions" ], "optionalPlugins": [ "alerting", + "charts", + "dashboard", "home", - "security", - "spaces", - "management", "licenseManagement", + "management", "maps", - "usageCollection", - "dashboard", - "charts" + "security", + "spaces", + "usageCollection" ], "server": true, "ui": true, "requiredBundles": [ - "esUiShared", - "kibanaUtils", - "kibanaReact", + "charts", "dashboard", - "savedObjects", + "esUiShared", + "fieldFormats", "home", + "kibanaReact", + "kibanaUtils", "maps", - "usageCollection", - "fieldFormats", - "charts" + "savedObjects", + "usageCollection" ], "extraPublicDirs": [ "common" diff --git a/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx b/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx index 82ae65b69d33a..e8e06dfce784b 100644 --- a/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx +++ b/x-pack/plugins/ml/public/application/components/job_spaces_list/job_spaces_list.tsx @@ -11,7 +11,8 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { JobType, - ML_SAVED_OBJECT_TYPE, + TrainedModelType, + ML_JOB_SAVED_OBJECT_TYPE, SavedObjectResult, } from '../../../../common/types/saved_objects'; import type { SpacesPluginStart, ShareToSpaceFlyoutProps } from '../../../../../spaces/public'; @@ -21,17 +22,21 @@ import { useToastNotificationService } from '../../services/toast_notification_s interface Props { spacesApi: SpacesPluginStart; spaceIds: string[]; - jobId: string; - jobType: JobType; + id: string; + jobType: JobType | TrainedModelType; refresh(): void; } const ALL_SPACES_ID = '*'; -const objectNoun = i18n.translate('xpack.ml.management.jobsSpacesList.objectNoun', { +const jobObjectNoun = i18n.translate('xpack.ml.management.jobsSpacesList.jobObjectNoun', { defaultMessage: 'job', }); -export const JobSpacesList: FC = ({ spacesApi, spaceIds, jobId, jobType, refresh }) => { +const modelObjectNoun = i18n.translate('xpack.ml.management.jobsSpacesList.modelObjectNoun', { + defaultMessage: 'trained model', +}); + +export const JobSpacesList: FC = ({ spacesApi, spaceIds, id, jobType, refresh }) => { const { displayErrorToast } = useToastNotificationService(); const [showFlyout, setShowFlyout] = useState(false); @@ -45,13 +50,18 @@ export const JobSpacesList: FC = ({ spacesApi, spaceIds, jobId, jobType, const spacesToRemove = spacesToAdd.includes(ALL_SPACES_ID) ? [] : spacesToMaybeRemove; if (spacesToAdd.length || spacesToRemove.length) { - const resp = await ml.savedObjects.updateJobsSpaces( - jobType, - [jobId], - spacesToAdd, - spacesToRemove - ); - handleApplySpaces(resp); + if (jobType === 'trained-model') { + const resp = await ml.savedObjects.updateModelsSpaces([id], spacesToAdd, spacesToRemove); + handleApplySpaces(resp); + } else { + const resp = await ml.savedObjects.updateJobsSpaces( + jobType, + [id], + spacesToAdd, + spacesToRemove + ); + handleApplySpaces(resp); + } } onClose(); } @@ -62,11 +72,11 @@ export const JobSpacesList: FC = ({ spacesApi, spaceIds, jobId, jobType, } function handleApplySpaces(resp: SavedObjectResult) { - Object.entries(resp).forEach(([id, { success, error }]) => { + Object.entries(resp).forEach(([errorId, { success, error }]) => { if (success === false) { const title = i18n.translate('xpack.ml.management.jobsSpacesList.updateSpaces.error', { defaultMessage: 'Error updating {id}', - values: { id }, + values: { id: errorId }, }); displayErrorToast(error, title); } @@ -80,11 +90,11 @@ export const JobSpacesList: FC = ({ spacesApi, spaceIds, jobId, jobType, const shareToSpaceFlyoutProps: ShareToSpaceFlyoutProps = { savedObjectTarget: { - type: ML_SAVED_OBJECT_TYPE, - id: jobId, + type: ML_JOB_SAVED_OBJECT_TYPE, + id, namespaces: spaceIds, - title: jobId, - noun: objectNoun, + title: id, + noun: jobType === 'trained-model' ? modelObjectNoun : jobObjectNoun, }, behaviorContext: 'outside-space', changeSpacesHandler, diff --git a/x-pack/plugins/ml/public/application/components/job_spaces_sync/job_spaces_sync_flyout.tsx b/x-pack/plugins/ml/public/application/components/job_spaces_sync/job_spaces_sync_flyout.tsx index 2131219deffdb..8b379dfbc7ac0 100644 --- a/x-pack/plugins/ml/public/application/components/job_spaces_sync/job_spaces_sync_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/job_spaces_sync/job_spaces_sync_flyout.tsx @@ -24,7 +24,7 @@ import { } from '@elastic/eui'; import { ml } from '../../services/ml_api_service'; -import { SyncSavedObjectResponse, SavedObjectResult } from '../../../../common/types/saved_objects'; +import { SyncSavedObjectResponse, SyncResult } from '../../../../common/types/saved_objects'; import { SyncList } from './sync_list'; import { useToastNotificationService } from '../../services/toast_notification_service'; @@ -74,7 +74,7 @@ export const JobSpacesSyncFlyout: FC = ({ onClose }) => { const { successCount, errorCount } = getResponseCounts(resp); if (errorCount > 0) { const title = i18n.translate('xpack.ml.management.syncSavedObjectsFlyout.sync.error', { - defaultMessage: 'Some jobs cannot be synchronized.', + defaultMessage: 'Some jobs or trained models cannot be synchronized.', }); displayErrorToast(resp as any, title); return; @@ -83,7 +83,7 @@ export const JobSpacesSyncFlyout: FC = ({ onClose }) => { displaySuccessToast( i18n.translate('xpack.ml.management.syncSavedObjectsFlyout.sync.success', { defaultMessage: - '{successCount} {successCount, plural, one {job} other {jobs}} synchronized', + '{successCount} {successCount, plural, one {item} other {items}} synchronized', values: { successCount }, }) ); @@ -153,13 +153,15 @@ export const JobSpacesSyncFlyout: FC = ({ onClose }) => { function getResponseCounts(resp: SyncSavedObjectResponse) { let successCount = 0; let errorCount = 0; - Object.values(resp).forEach((result: SavedObjectResult) => { - Object.values(result).forEach(({ success, error }) => { - if (success === true) { - successCount++; - } else if (error !== undefined) { - errorCount++; - } + Object.values(resp).forEach((result: SyncResult) => { + Object.values(result).forEach((type) => { + Object.values(type).forEach(({ success, error }) => { + if (success === true) { + successCount++; + } else if (error !== undefined) { + errorCount++; + } + }); }); }); return { successCount, errorCount }; diff --git a/x-pack/plugins/ml/public/application/components/job_spaces_sync/sync_list.tsx b/x-pack/plugins/ml/public/application/components/job_spaces_sync/sync_list.tsx index bdf80db21364d..8d724f5b6187e 100644 --- a/x-pack/plugins/ml/public/application/components/job_spaces_sync/sync_list.tsx +++ b/x-pack/plugins/ml/public/application/components/job_spaces_sync/sync_list.tsx @@ -5,12 +5,19 @@ * 2.0. */ -import React, { FC } from 'react'; +import React, { FC, Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiText, EuiTitle, EuiAccordion, EuiTextColor, EuiHorizontalRule } from '@elastic/eui'; +import { + EuiText, + EuiTitle, + EuiAccordion, + EuiTextColor, + EuiHorizontalRule, + EuiSpacer, +} from '@elastic/eui'; -import { SyncSavedObjectResponse } from '../../../../common/types/saved_objects'; +import type { SyncSavedObjectResponse, SyncResult } from '../../../../common/types/saved_objects'; export const SyncList: FC<{ syncItems: SyncSavedObjectResponse | null }> = ({ syncItems }) => { if (syncItems === null) { @@ -39,17 +46,17 @@ export const SyncList: FC<{ syncItems: SyncSavedObjectResponse | null }> = ({ sy }; const SavedObjectsCreated: FC<{ syncItems: SyncSavedObjectResponse }> = ({ syncItems }) => { - const items = Object.keys(syncItems.savedObjectsCreated); + const count = getTotalItemsCount(syncItems.savedObjectsCreated); const title = ( <>

- +

@@ -66,21 +73,23 @@ const SavedObjectsCreated: FC<{ syncItems: SyncSavedObjectResponse }> = ({ syncI ); - return ; + return ( + + ); }; const SavedObjectsDeleted: FC<{ syncItems: SyncSavedObjectResponse }> = ({ syncItems }) => { - const items = Object.keys(syncItems.savedObjectsDeleted); + const count = getTotalItemsCount(syncItems.savedObjectsDeleted); const title = ( <>

- +

@@ -97,21 +106,23 @@ const SavedObjectsDeleted: FC<{ syncItems: SyncSavedObjectResponse }> = ({ syncI ); - return ; + return ( + + ); }; const DatafeedsAdded: FC<{ syncItems: SyncSavedObjectResponse }> = ({ syncItems }) => { - const items = Object.keys(syncItems.datafeedsAdded); + const count = getTotalItemsCount(syncItems.datafeedsAdded); const title = ( <>

- +

@@ -128,21 +139,21 @@ const DatafeedsAdded: FC<{ syncItems: SyncSavedObjectResponse }> = ({ syncItems ); - return ; + return ; }; const DatafeedsRemoved: FC<{ syncItems: SyncSavedObjectResponse }> = ({ syncItems }) => { - const items = Object.keys(syncItems.datafeedsRemoved); + const count = getTotalItemsCount(syncItems.datafeedsRemoved); const title = ( <>

- +

@@ -159,21 +170,35 @@ const DatafeedsRemoved: FC<{ syncItems: SyncSavedObjectResponse }> = ({ syncItem ); - return ; + return ; }; -const SyncItem: FC<{ id: string; title: JSX.Element; items: string[] }> = ({ +const SyncItem: FC<{ id: string; title: JSX.Element; results: SyncResult }> = ({ id, title, - items, -}) => ( - - -
    - {items.map((item) => ( -
  • {item}
  • - ))} -
-
-
-); + results, +}) => { + return ( + + {Object.entries(results).map(([type, items]) => { + return ( + + +

{type}

+
    + {Object.keys(items).map((item) => ( +
  • {item}
  • + ))} +
+
+ +
+ ); + })} +
+ ); +}; + +function getTotalItemsCount(result: SyncResult) { + return Object.values(result).flatMap((r) => Object.keys(r)).length; +} diff --git a/x-pack/plugins/ml/public/application/components/saved_objects_warning/saved_objects_warning.tsx b/x-pack/plugins/ml/public/application/components/saved_objects_warning/saved_objects_warning.tsx index a63479bf3e76d..028b9db615147 100644 --- a/x-pack/plugins/ml/public/application/components/saved_objects_warning/saved_objects_warning.tsx +++ b/x-pack/plugins/ml/public/application/components/saved_objects_warning/saved_objects_warning.tsx @@ -8,13 +8,13 @@ import React, { FC, useEffect, useState, useCallback, useRef, useMemo } from 'react'; import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { JobType } from '../../../../common/types/saved_objects'; +import type { JobType, TrainedModelType } from '../../../../common/types/saved_objects'; import { useMlApiContext } from '../../contexts/kibana'; import { JobSpacesSyncFlyout } from '../../components/job_spaces_sync'; import { checkPermission } from '../../capabilities/check_capabilities'; interface Props { - jobType?: JobType; + jobType?: JobType | TrainedModelType; onCloseFlyout?: () => void; forceRefresh?: boolean; } @@ -55,14 +55,14 @@ export const SavedObjectsWarning: FC = ({ jobType, onCloseFlyout, forceRe mounted.current = false; }; }, - [forceRefresh, mounted] + [forceRefresh, mounted, checkStatus] ); const onClose = useCallback(() => { + setShowSyncFlyout(false); if (forceRefresh === undefined) { checkStatus(); } - setShowSyncFlyout(false); if (typeof onCloseFlyout === 'function') { onCloseFlyout(); } @@ -83,7 +83,7 @@ export const SavedObjectsWarning: FC = ({ jobType, onCloseFlyout, forceRe title={ } color="warning" @@ -93,7 +93,7 @@ export const SavedObjectsWarning: FC = ({ jobType, onCloseFlyout, forceRe <> {canCreateJob ? ( diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 71a6b7d88fe1a..18826667298e5 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -318,7 +318,7 @@ export class JobsList extends Component { diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index 083982e8fccd4..fe43aa9d3b123 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -38,6 +38,10 @@ import { getDocLinks } from '../../../../util/dependency_cache'; // @ts-ignore undeclared module import { JobsListView } from '../../../../jobs/jobs_list/components/jobs_list_view/index'; import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/analytics_management/components/analytics_list'; +import { + ModelsList, + getDefaultModelsListState, +} from '../../../../trained_models/models_management/models_list'; import { AccessDeniedPage } from '../access_denied_page'; import { InsufficientLicensePage } from '../insufficient_license_page'; import type { SharePluginStart } from '../../../../../../../../../src/plugins/share/public'; @@ -48,7 +52,8 @@ import { getMlGlobalServices } from '../../../../app'; import { ListingPageUrlState } from '../../../../../../common/types/common'; import { getDefaultDFAListState } from '../../../../data_frame_analytics/pages/analytics_management/page'; import { ExportJobsFlyout, ImportJobsFlyout } from '../../../../components/import_export_jobs'; -import type { JobType } from '../../../../../../common/types/saved_objects'; +import type { JobType, TrainedModelType } from '../../../../../../common/types/saved_objects'; +import type { FieldFormatsStart } from '../../../../../../../../../src/plugins/field_formats/public'; interface Tab extends EuiTabbedContentTab { 'data-test-subj': string; @@ -77,6 +82,7 @@ const getEmptyFunctionComponent: React.FC = ({ children }) = function useTabs(isMlEnabledInSpace: boolean, spacesApi: SpacesPluginStart | undefined): Tab[] { const [adPageState, updateAdPageState] = usePageState(getDefaultAnomalyDetectionJobsListState()); const [dfaPageState, updateDfaPageState] = usePageState(getDefaultDFAListState()); + const [modelListState, updateModelListState] = usePageState(getDefaultModelsListState()); return useMemo( () => [ @@ -118,8 +124,33 @@ function useTabs(isMlEnabledInSpace: boolean, spacesApi: SpacesPluginStart | und ), }, + { + 'data-test-subj': 'mlStackManagementJobsListAnalyticsTab', + id: 'trained-model', + name: i18n.translate('xpack.ml.management.jobsList.trainedModelsTab', { + defaultMessage: 'Trained models', + }), + content: ( + + + + + ), + }, ], - [isMlEnabledInSpace, adPageState, updateAdPageState, dfaPageState, updateDfaPageState] + [ + isMlEnabledInSpace, + adPageState, + updateAdPageState, + dfaPageState, + updateDfaPageState, + modelListState, + updateModelListState, + ] ); } @@ -130,7 +161,8 @@ export const JobsListPage: FC<{ spacesApi?: SpacesPluginStart; data: DataPublicPluginStart; usageCollection?: UsageCollectionSetup; -}> = ({ coreStart, share, history, spacesApi, data, usageCollection }) => { + fieldFormats: FieldFormatsStart; +}> = ({ coreStart, share, history, spacesApi, data, usageCollection, fieldFormats }) => { const spacesEnabled = spacesApi !== undefined; const [initialized, setInitialized] = useState(false); const [accessDenied, setAccessDenied] = useState(false); @@ -138,7 +170,7 @@ export const JobsListPage: FC<{ const [showSyncFlyout, setShowSyncFlyout] = useState(false); const [isMlEnabledInSpace, setIsMlEnabledInSpace] = useState(false); const tabs = useTabs(isMlEnabledInSpace, spacesApi); - const [currentTabId, setCurrentTabId] = useState('anomaly-detector'); + const [currentTabId, setCurrentTabId] = useState('anomaly-detector'); const I18nContext = coreStart.i18n.Context; const theme$ = coreStart.theme.theme$; @@ -228,6 +260,8 @@ export const JobsListPage: FC<{ share, data, usageCollection, + fieldFormats, + spacesApi, mlServices: getMlGlobalServices(coreStart.http, usageCollection), }} > @@ -274,7 +308,12 @@ export const JobsListPage: FC<{ )}
- + diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/index.ts b/x-pack/plugins/ml/public/application/management/jobs_list/index.ts index b1dd8344b86fd..547f8c8b622ad 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/index.ts +++ b/x-pack/plugins/ml/public/application/management/jobs_list/index.ts @@ -18,6 +18,7 @@ import { setDependencyCache, clearCache } from '../../util/dependency_cache'; import './_index.scss'; import type { SharePluginStart } from '../../../../../../../src/plugins/share/public'; import type { SpacesPluginStart } from '../../../../../spaces/public'; +import type { FieldFormatsStart } from '../../../../../../../src/plugins/field_formats/public'; const renderApp = ( element: HTMLElement, @@ -25,6 +26,7 @@ const renderApp = ( coreStart: CoreStart, share: SharePluginStart, data: DataPublicPluginStart, + fieldFormats: FieldFormatsStart, spacesApi?: SpacesPluginStart, usageCollection?: UsageCollectionSetup ) => { @@ -36,6 +38,7 @@ const renderApp = ( data, spacesApi, usageCollection, + fieldFormats, }), element ); @@ -66,6 +69,7 @@ export async function mountApp( coreStart, pluginsStart.share, pluginsStart.data, + pluginsStart.fieldFormats, pluginsStart.spaces, deps.usageCollection ); diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/saved_objects.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/saved_objects.ts index b256f4530ff08..ff43cb75653a8 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/saved_objects.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/saved_objects.ts @@ -7,16 +7,21 @@ // Service for managing job saved objects +import { useMemo } from 'react'; +import { useMlKibana } from '../../contexts/kibana'; + import { HttpService } from '../http_service'; import { basePath } from './index'; import { JobType, + TrainedModelType, CanDeleteJobResponse, SyncSavedObjectResponse, InitializeSavedObjectResponse, SavedObjectResult, JobsSpacesResponse, + TrainedModelsSpacesResponse, SyncCheckResponse, } from '../../../../common/types/saved_objects'; @@ -62,7 +67,7 @@ export const savedObjectsApiProvider = (httpService: HttpService) => ({ query: { simulate }, }); }, - syncCheck(jobType?: JobType) { + syncCheck(jobType?: JobType | TrainedModelType) { const body = JSON.stringify({ jobType }); return httpService.http({ path: `${basePath()}/saved_objects/sync_check`, @@ -78,4 +83,32 @@ export const savedObjectsApiProvider = (httpService: HttpService) => ({ body, }); }, + trainedModelsSpaces() { + return httpService.http({ + path: `${basePath()}/saved_objects/trained_models_spaces`, + method: 'GET', + }); + }, + updateModelsSpaces(modelIds: string[], spacesToAdd: string[], spacesToRemove: string[]) { + const body = JSON.stringify({ modelIds, spacesToAdd, spacesToRemove }); + return httpService.http({ + path: `${basePath()}/saved_objects/update_trained_models_spaces`, + method: 'POST', + body, + }); + }, }); + +type SavedObjectsApiService = ReturnType; + +/** + * Hooks for accessing {@link TrainedModelsApiService} in React components. + */ +export function useSavedObjectsApiService(): SavedObjectsApiService { + const { + services: { + mlServices: { httpService }, + }, + } = useMlKibana(); + return useMemo(() => savedObjectsApiProvider(httpService), [httpService]); +} diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts index 8ea7ff07345ee..dcadb1ab2c50f 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts @@ -61,13 +61,10 @@ export function trainedModelsApiProvider(httpService: HttpService) { * @param params - Optional query params */ getTrainedModels(modelId?: string | string[], params?: InferenceQueryParams) { - let model = modelId ?? ''; - if (Array.isArray(modelId)) { - model = modelId.join(','); - } + const model = Array.isArray(modelId) ? modelId.join(',') : modelId; return httpService.http({ - path: `${apiBasePath}/trained_models${model && `/${model}`}`, + path: `${apiBasePath}/trained_models${model ? `/${model}` : ''}`, method: 'GET', ...(params ? { query: params as HttpFetchQuery } : {}), }); @@ -81,10 +78,10 @@ export function trainedModelsApiProvider(httpService: HttpService) { * @param params - Optional query params */ getTrainedModelStats(modelId?: string | string[], params?: InferenceStatsQueryParams) { - const model = (Array.isArray(modelId) ? modelId.join(',') : modelId) || '_all'; + const model = Array.isArray(modelId) ? modelId.join(',') : modelId; return httpService.http({ - path: `${apiBasePath}/trained_models/${model}/_stats`, + path: `${apiBasePath}/trained_models${model ? `/${model}` : ''}/_stats`, method: 'GET', }); }, diff --git a/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx b/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx index 97a6fd3eb7b27..9f399046751e9 100644 --- a/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/trained_models/models_management/models_list.tsx @@ -17,6 +17,7 @@ import { EuiSpacer, EuiTitle, SearchFilterConfig, + EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -29,6 +30,7 @@ import { ModelsTableToConfigMapping } from './index'; import { ModelsBarStats, StatsBar } from '../../components/stats_bar'; import { useMlKibana, useMlLocator, useNavigateToPath, useTimefilter } from '../../contexts/kibana'; import { useTrainedModelsApiService } from '../../services/ml_api_service/trained_models'; +import { useSavedObjectsApiService } from '../../services/ml_api_service/saved_objects'; import { ModelPipelines, TrainedModelConfigResponse, @@ -47,8 +49,10 @@ import { useToastNotificationService } from '../../services/toast_notification_s import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter'; import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common'; import { useRefresh } from '../../routing/use_refresh'; -import { DEPLOYMENT_STATE } from '../../../../common/constants/trained_models'; +import { DEPLOYMENT_STATE, TRAINED_MODEL_TYPE } from '../../../../common/constants/trained_models'; import { getUserConfirmationProvider } from './force_stop_dialog'; +import { JobSpacesList } from '../../components/job_spaces_list'; +import { SavedObjectsWarning } from '../../components/saved_objects_warning'; type Stats = Omit; @@ -72,12 +76,23 @@ export const BUILT_IN_MODEL_TYPE = i18n.translate( { defaultMessage: 'built-in' } ); -export const ModelsList: FC = () => { +interface Props { + isManagementTable?: boolean; + pageState?: ListingPageUrlState; + updatePageState?: (update: Partial) => void; +} + +export const ModelsList: FC = ({ + isManagementTable = false, + pageState: pageStateExternal, + updatePageState: updatePageStateExternal, +}) => { const { services: { application: { navigateToUrl, capabilities }, overlays, theme, + spacesApi, }, } = useMlKibana(); const urlLocator = useMlLocator()!; @@ -86,18 +101,28 @@ export const ModelsList: FC = () => { const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE); - const [pageState, updatePageState] = usePageUrlState( + // allow for an internally controlled page state which stores the state in the URL + // or an external page state, which is passed in as a prop. + // external page state is used on the management page. + const [pageStateInternal, updatePageStateInternal] = usePageUrlState( ML_PAGES.TRAINED_MODELS_MANAGE, getDefaultModelsListState() ); + const [pageState, updatePageState] = + pageStateExternal && updatePageStateExternal + ? [pageStateExternal, updatePageStateExternal] + : [pageStateInternal, updatePageStateInternal]; + const refresh = useRefresh(); const searchQueryText = pageState.queryText ?? ''; - const canDeleteDataFrameAnalytics = capabilities.ml.canDeleteDataFrameAnalytics as boolean; + const canDeleteTrainedModels = capabilities.ml.canDeleteTrainedModels as boolean; + const canStartStopTrainedModels = capabilities.ml.canStartStopTrainedModels as boolean; const trainedModelsApiService = useTrainedModelsApiService(); + const savedObjectsApiService = useSavedObjectsApiService(); const { displayErrorToast, displayDangerToast, displaySuccessToast } = useToastNotificationService(); @@ -106,6 +131,7 @@ export const ModelsList: FC = () => { const [items, setItems] = useState([]); const [selectedModels, setSelectedModels] = useState([]); const [modelsToDelete, setModelsToDelete] = useState([]); + const [modelSpaces, setModelSpaces] = useState<{ [modelId: string]: string[] }>({}); const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( {} ); @@ -123,11 +149,16 @@ export const ModelsList: FC = () => { * Fetches trained models. */ const fetchModelsData = useCallback(async () => { + setIsLoading(true); try { const response = await trainedModelsApiService.getTrainedModels(undefined, { with_pipelines: true, size: 1000, }); + if (isManagementTable) { + const { trainedModels } = await savedObjectsApiService.trainedModelsSpaces(); + setModelSpaces(trainedModels); + } const newItems: ModelItem[] = []; const expandedItemsToRefresh = []; @@ -154,7 +185,9 @@ export const ModelsList: FC = () => { } // Need to fetch state for 3rd party models to enable/disable actions - await fetchModelsStats(newItems.filter((v) => v.model_type.includes('pytorch'))); + await fetchModelsStats( + newItems.filter((v) => v.model_type.includes(TRAINED_MODEL_TYPE.PYTORCH)) + ); setItems(newItems); @@ -357,125 +390,141 @@ export const ModelsList: FC = () => { await navigateToPath(path, false); }, }, - { - name: i18n.translate('xpack.ml.inference.modelsList.startModelDeploymentActionLabel', { - defaultMessage: 'Start deployment', - }), - description: i18n.translate('xpack.ml.inference.modelsList.startModelDeploymentActionLabel', { - defaultMessage: 'Start deployment', - }), - icon: 'play', - type: 'icon', - isPrimary: true, - enabled: (item) => { - const { state } = item.stats?.deployment_stats ?? {}; - return ( - !isLoading && state !== DEPLOYMENT_STATE.STARTED && state !== DEPLOYMENT_STATE.STARTING - ); - }, - available: (item) => item.model_type === 'pytorch', - onClick: async (item) => { - try { - setIsLoading(true); - await trainedModelsApiService.startModelAllocation(item.model_id); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.startSuccess', { - defaultMessage: 'Deployment for "{modelId}" has been started successfully.', - values: { - modelId: item.model_id, - }, - }) - ); - await fetchModelsData(); - } catch (e) { - displayErrorToast( - e, - i18n.translate('xpack.ml.trainedModels.modelsList.startFailed', { - defaultMessage: 'Failed to start "{modelId}"', - values: { - modelId: item.model_id, - }, - }) - ); - setIsLoading(false); - } - }, - }, - { - name: i18n.translate('xpack.ml.inference.modelsList.stopModelDeploymentActionLabel', { - defaultMessage: 'Stop deployment', - }), - description: i18n.translate('xpack.ml.inference.modelsList.stopModelDeploymentActionLabel', { - defaultMessage: 'Stop deployment', - }), - icon: 'stop', - type: 'icon', - isPrimary: true, - available: (item) => item.model_type === 'pytorch', - enabled: (item) => - !isLoading && - isPopulatedObject(item.stats?.deployment_stats) && - item.stats?.deployment_stats?.state !== DEPLOYMENT_STATE.STOPPING, - onClick: async (item) => { - const requireForceStop = isPopulatedObject(item.pipelines); - - if (requireForceStop) { - const hasUserApproved = await getUserConfirmation(item); - if (!hasUserApproved) return; - } - - try { - setIsLoading(true); - await trainedModelsApiService.stopModelAllocation(item.model_id, { - force: requireForceStop, - }); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.stopSuccess', { - defaultMessage: 'Deployment for "{modelId}" has been stopped successfully.', - values: { - modelId: item.model_id, - }, - }) - ); - // Need to fetch model state updates - await fetchModelsData(); - } catch (e) { - displayErrorToast( - e, - i18n.translate('xpack.ml.trainedModels.modelsList.stopFailed', { - defaultMessage: 'Failed to stop "{modelId}"', - values: { - modelId: item.model_id, - }, - }) - ); - setIsLoading(false); - } - }, - }, - { - name: i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { - defaultMessage: 'Delete model', - }), - description: i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { - defaultMessage: 'Delete model', - }), - 'data-test-subj': 'mlModelsTableRowDeleteAction', - icon: 'trash', - type: 'icon', - color: 'danger', - isPrimary: false, - onClick: async (model) => { - await prepareModelsForDeletion([model]); - }, - available: (item) => canDeleteDataFrameAnalytics && !isBuiltInModel(item), - enabled: (item) => { - // TODO check for permissions to delete ingest pipelines. - // ATM undefined means pipelines fetch failed server-side. - return !isPopulatedObject(item.pipelines); - }, - }, ]; + if (isManagementTable === false) { + actions.push( + ...([ + { + name: i18n.translate('xpack.ml.inference.modelsList.startModelDeploymentActionLabel', { + defaultMessage: 'Start deployment', + }), + description: i18n.translate( + 'xpack.ml.inference.modelsList.startModelDeploymentActionLabel', + { + defaultMessage: 'Start deployment', + } + ), + icon: 'play', + type: 'icon', + isPrimary: true, + enabled: (item) => { + const { state } = item.stats?.deployment_stats ?? {}; + return ( + canStartStopTrainedModels && + !isLoading && + state !== DEPLOYMENT_STATE.STARTED && + state !== DEPLOYMENT_STATE.STARTING + ); + }, + available: (item) => item.model_type === TRAINED_MODEL_TYPE.PYTORCH, + onClick: async (item) => { + try { + setIsLoading(true); + await trainedModelsApiService.startModelAllocation(item.model_id); + displaySuccessToast( + i18n.translate('xpack.ml.trainedModels.modelsList.startSuccess', { + defaultMessage: 'Deployment for "{modelId}" has been started successfully.', + values: { + modelId: item.model_id, + }, + }) + ); + await fetchModelsData(); + } catch (e) { + displayErrorToast( + e, + i18n.translate('xpack.ml.trainedModels.modelsList.startFailed', { + defaultMessage: 'Failed to start "{modelId}"', + values: { + modelId: item.model_id, + }, + }) + ); + setIsLoading(false); + } + }, + }, + { + name: i18n.translate('xpack.ml.inference.modelsList.stopModelDeploymentActionLabel', { + defaultMessage: 'Stop deployment', + }), + description: i18n.translate( + 'xpack.ml.inference.modelsList.stopModelDeploymentActionLabel', + { + defaultMessage: 'Stop deployment', + } + ), + icon: 'stop', + type: 'icon', + isPrimary: true, + available: (item) => item.model_type === TRAINED_MODEL_TYPE.PYTORCH, + enabled: (item) => + canStartStopTrainedModels && + !isLoading && + isPopulatedObject(item.stats?.deployment_stats) && + item.stats?.deployment_stats?.state !== DEPLOYMENT_STATE.STOPPING, + onClick: async (item) => { + const requireForceStop = isPopulatedObject(item.pipelines); + + if (requireForceStop) { + const hasUserApproved = await getUserConfirmation(item); + if (!hasUserApproved) return; + } + + try { + setIsLoading(true); + await trainedModelsApiService.stopModelAllocation(item.model_id, { + force: requireForceStop, + }); + displaySuccessToast( + i18n.translate('xpack.ml.trainedModels.modelsList.stopSuccess', { + defaultMessage: 'Deployment for "{modelId}" has been stopped successfully.', + values: { + modelId: item.model_id, + }, + }) + ); + // Need to fetch model state updates + await fetchModelsData(); + } catch (e) { + displayErrorToast( + e, + i18n.translate('xpack.ml.trainedModels.modelsList.stopFailed', { + defaultMessage: 'Failed to stop "{modelId}"', + values: { + modelId: item.model_id, + }, + }) + ); + setIsLoading(false); + } + }, + }, + { + name: i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { + defaultMessage: 'Delete model', + }), + description: i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { + defaultMessage: 'Delete model', + }), + 'data-test-subj': 'mlModelsTableRowDeleteAction', + icon: 'trash', + type: 'icon', + color: 'danger', + isPrimary: false, + onClick: async (model) => { + await prepareModelsForDeletion([model]); + }, + available: (item) => canDeleteTrainedModels && !isBuiltInModel(item), + enabled: (item) => { + // TODO check for permissions to delete ingest pipelines. + // ATM undefined means pipelines fetch failed server-side. + return !isPopulatedObject(item.pipelines); + }, + }, + ] as Array>) + ); + } const toggleDetails = async (item: ModelItem) => { const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; @@ -582,6 +631,29 @@ export const ModelsList: FC = () => { }, ]; + if (isManagementTable) { + columns.splice(columns.length - 1, 0, { + field: ModelsTableToConfigMapping.id, + name: i18n.translate('xpack.ml.trainedModels.modelsList.spacesLabel', { + defaultMessage: 'Spaces', + }), + render: (id: string) => { + const spaces = modelSpaces[id]; + return ( + + ); + }, + sortable: false, + 'data-test-subj': 'mlModelsTableColumnSpacesLabel', + }); + } + const filters: SearchFilterConfig[] = inferenceTypesOptions && inferenceTypesOptions.length > 0 ? [ @@ -623,7 +695,7 @@ export const ModelsList: FC = () => { ); - const isSelectionAllowed = canDeleteDataFrameAnalytics; + const isSelectionAllowed = canDeleteTrainedModels; const selection: EuiTableSelectionType | undefined = isSelectionAllowed ? { @@ -686,12 +758,27 @@ export const ModelsList: FC = () => { return ( <> - + {isManagementTable ? null : ( + <> + + + )} {modelsStats && ( - - - + <> + + + + {isManagementTable ? ( + + + + ) : null} + )} @@ -707,7 +794,7 @@ export const ModelsList: FC = () => { itemId={ModelsTableToConfigMapping.id} loading={isLoading} search={search} - selection={selection} + selection={isManagementTable ? undefined : selection} rowProps={(item) => ({ 'data-test-subj': `mlModelsTableRow row-${item.model_id}`, })} @@ -731,3 +818,21 @@ export const ModelsList: FC = () => { ); }; + +export const RefreshModelsListButton: FC<{ refresh: () => Promise; isLoading: boolean }> = ({ + refresh, + isLoading, +}) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts index c853179e7121f..654c3db184aa0 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts @@ -47,7 +47,7 @@ describe('check_capabilities', () => { ); const { capabilities } = await getCapabilities(); const count = Object.keys(capabilities).length; - expect(count).toBe(32); + expect(count).toBe(36); }); }); @@ -75,6 +75,7 @@ describe('check_capabilities', () => { expect(capabilities.canCreateAnnotation).toBe(true); expect(capabilities.canDeleteAnnotation).toBe(true); expect(capabilities.canUseMlAlerts).toBe(true); + expect(capabilities.canGetTrainedModels).toBe(true); expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); @@ -98,6 +99,9 @@ describe('check_capabilities', () => { expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); expect(capabilities.canCreateMlAlerts).toBe(false); expect(capabilities.canViewMlNodes).toBe(false); + expect(capabilities.canCreateTrainedModels).toBe(false); + expect(capabilities.canDeleteTrainedModels).toBe(false); + expect(capabilities.canStartStopTrainedModels).toBe(false); }); test('full capabilities', async () => { @@ -122,6 +126,8 @@ describe('check_capabilities', () => { expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(true); expect(capabilities.canDeleteAnnotation).toBe(true); + expect(capabilities.canUseMlAlerts).toBe(true); + expect(capabilities.canGetTrainedModels).toBe(true); expect(capabilities.canCreateJob).toBe(true); expect(capabilities.canDeleteJob).toBe(true); @@ -143,7 +149,11 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(true); expect(capabilities.canCreateDataFrameAnalytics).toBe(true); expect(capabilities.canStartStopDataFrameAnalytics).toBe(true); + expect(capabilities.canCreateMlAlerts).toBe(true); expect(capabilities.canViewMlNodes).toBe(true); + expect(capabilities.canCreateTrainedModels).toBe(true); + expect(capabilities.canDeleteTrainedModels).toBe(true); + expect(capabilities.canStartStopTrainedModels).toBe(true); }); test('upgrade in progress with full capabilities', async () => { @@ -168,6 +178,8 @@ describe('check_capabilities', () => { expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(false); expect(capabilities.canDeleteAnnotation).toBe(false); + expect(capabilities.canUseMlAlerts).toBe(false); + expect(capabilities.canGetTrainedModels).toBe(true); expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); @@ -189,6 +201,11 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateMlAlerts).toBe(false); + expect(capabilities.canViewMlNodes).toBe(false); + expect(capabilities.canCreateTrainedModels).toBe(false); + expect(capabilities.canDeleteTrainedModels).toBe(false); + expect(capabilities.canStartStopTrainedModels).toBe(false); }); test('upgrade in progress with partial capabilities', async () => { @@ -213,6 +230,8 @@ describe('check_capabilities', () => { expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(false); expect(capabilities.canDeleteAnnotation).toBe(false); + expect(capabilities.canUseMlAlerts).toBe(false); + expect(capabilities.canGetTrainedModels).toBe(true); expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); @@ -234,6 +253,11 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateMlAlerts).toBe(false); + expect(capabilities.canViewMlNodes).toBe(false); + expect(capabilities.canCreateTrainedModels).toBe(false); + expect(capabilities.canDeleteTrainedModels).toBe(false); + expect(capabilities.canStartStopTrainedModels).toBe(false); }); test('full capabilities, ml disabled in space', async () => { @@ -258,6 +282,8 @@ describe('check_capabilities', () => { expect(capabilities.canGetAnnotations).toBe(false); expect(capabilities.canCreateAnnotation).toBe(false); expect(capabilities.canDeleteAnnotation).toBe(false); + expect(capabilities.canUseMlAlerts).toBe(false); + expect(capabilities.canGetTrainedModels).toBe(false); expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); @@ -279,6 +305,11 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateMlAlerts).toBe(false); + expect(capabilities.canViewMlNodes).toBe(false); + expect(capabilities.canCreateTrainedModels).toBe(false); + expect(capabilities.canDeleteTrainedModels).toBe(false); + expect(capabilities.canStartStopTrainedModels).toBe(false); }); }); @@ -305,6 +336,8 @@ describe('check_capabilities', () => { expect(capabilities.canGetAnnotations).toBe(false); expect(capabilities.canCreateAnnotation).toBe(false); expect(capabilities.canDeleteAnnotation).toBe(false); + expect(capabilities.canUseMlAlerts).toBe(false); + expect(capabilities.canGetTrainedModels).toBe(false); expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); @@ -326,5 +359,10 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); + expect(capabilities.canCreateMlAlerts).toBe(false); + expect(capabilities.canViewMlNodes).toBe(false); + expect(capabilities.canCreateTrainedModels).toBe(false); + expect(capabilities.canDeleteTrainedModels).toBe(false); + expect(capabilities.canStartStopTrainedModels).toBe(false); }); }); diff --git a/x-pack/plugins/ml/server/lib/ml_client/errors.ts b/x-pack/plugins/ml/server/lib/ml_client/errors.ts index 23e5446138067..c9d2158e00068 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/errors.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/errors.ts @@ -5,6 +5,8 @@ * 2.0. */ +/* eslint-disable max-classes-per-file */ + export class MLJobNotFound extends Error { statusCode = 404; constructor(message?: string) { @@ -12,3 +14,11 @@ export class MLJobNotFound extends Error { Object.setPrototypeOf(this, new.target.prototype); } } + +export class MLModelNotFound extends Error { + statusCode = 404; + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} diff --git a/x-pack/plugins/ml/server/lib/ml_client/index.ts b/x-pack/plugins/ml/server/lib/ml_client/index.ts index b5329ba2cfbca..03fc5e6244b31 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/index.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/index.ts @@ -6,5 +6,5 @@ */ export { getMlClient } from './ml_client'; -export { MLJobNotFound } from './errors'; +export { MLJobNotFound, MLModelNotFound } from './errors'; export type { MlClient } from './types'; diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index 3dc71cc64add9..11d5de7c45ed8 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -5,21 +5,24 @@ * 2.0. */ +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IScopedClusterClient } from 'kibana/server'; import { JobSavedObjectService } from '../../saved_objects'; +import { getJobDetailsFromTrainedModel } from '../../saved_objects/util'; import { JobType } from '../../../common/types/saved_objects'; import { Job, Datafeed } from '../../../common/types/anomaly_detection_jobs'; import { searchProvider } from './search'; import { DataFrameAnalyticsConfig } from '../../../common/types/data_frame_analytics'; -import { MLJobNotFound } from './errors'; +import { MLJobNotFound, MLModelNotFound } from './errors'; import { MlClient, MlClientParams, MlGetADParams, MlGetDFAParams, MlGetDatafeedParams, + MlGetTrainedModelParams, } from './types'; export function getMlClient( @@ -32,11 +35,11 @@ export function getMlClient( const jobIds = jobType === 'anomaly-detector' ? getADJobIdsFromRequest(p) : getDFAJobIdsFromRequest(p); if (jobIds.length) { - await checkIds(jobType, jobIds, allowWildcards); + await checkJobIds(jobType, jobIds, allowWildcards); } } - async function checkIds(jobType: JobType, jobIds: string[], allowWildcards: boolean = false) { + async function checkJobIds(jobType: JobType, jobIds: string[], allowWildcards: boolean = false) { const filteredJobIds = await jobSavedObjectService.filterJobIdsForSpace(jobType, jobIds); let missingIds = jobIds.filter((j) => filteredJobIds.indexOf(j) === -1); if (allowWildcards === true && missingIds.join().match('\\*') !== null) { @@ -90,7 +93,7 @@ export function getMlClient( // check the remaining jobs ids if (requestedJobIds.length) { - await checkIds('anomaly-detector', requestedJobIds, true); + await checkJobIds('anomaly-detector', requestedJobIds, true); } } } @@ -100,7 +103,7 @@ export function getMlClient( ...p: Parameters ) { // similar to groupIdsCheck above, however we need to load the jobs first to get the groups information - const ids = getADJobIdsFromRequest(p); + const ids = filterAll(getADJobIdsFromRequest(p)); if (ids.length) { const body = await mlClient.getJobs(...p); await groupIdsCheck(p, body.jobs, filteredJobIds); @@ -124,6 +127,25 @@ export function getMlClient( } } + async function modelIdsCheck(p: MlClientParams, allowWildcards: boolean = false) { + const modelIds = filterAll(getModelIdsFromRequest(p)); + if (modelIds.length) { + await checkModelIds(modelIds, allowWildcards); + } + } + + async function checkModelIds(modelIds: string[], allowWildcards: boolean = false) { + const filteredModelIds = await jobSavedObjectService.filterTrainedModelIdsForSpace(modelIds); + let missingIds = modelIds.filter((j) => filteredModelIds.indexOf(j) === -1); + if (allowWildcards === true && missingIds.join().match('\\*') !== null) { + // filter out wildcard ids from the error + missingIds = missingIds.filter((id) => id.match('\\*') === null); + } + if (missingIds.length) { + throw new MLModelNotFound(`No known model with id '${missingIds.join(',')}'`); + } + } + // @ts-expect-error promise and TransportRequestPromise are incompatible. missing abort return { async closeJob(...p: Parameters) { @@ -178,6 +200,7 @@ export function getMlClient( return mlClient.deleteModelSnapshot(...p); }, async deleteTrainedModel(...p: Parameters) { + await modelIdsCheck(p); return mlClient.deleteTrainedModel(...p); }, async estimateModelMemory(...p: Parameters) { @@ -432,15 +455,45 @@ export function getMlClient( return mlClient.getRecords(...p); }, async getTrainedModels(...p: Parameters) { - return mlClient.getTrainedModels(...p); + await modelIdsCheck(p, true); + try { + const body = await mlClient.getTrainedModels(...p); + const models = + await jobSavedObjectService.filterTrainedModelsForSpace( + body.trained_model_configs, + 'model_id' + ); + return { ...body, count: models.length, trained_model_configs: models }; + } catch (error) { + if (error.statusCode === 404) { + throw new MLModelNotFound(error.body.error.reason); + } + throw error.body ?? error; + } }, async getTrainedModelsStats(...p: Parameters) { - return mlClient.getTrainedModelsStats(...p); + await modelIdsCheck(p, true); + try { + const body = await mlClient.getTrainedModelsStats(...p); + const models = + await jobSavedObjectService.filterTrainedModelsForSpace( + body.trained_model_stats, + 'model_id' + ); + return { ...body, count: models.length, trained_model_stats: models }; + } catch (error) { + if (error.statusCode === 404) { + throw new MLModelNotFound(error.body.error.reason); + } + throw error.body ?? error; + } }, async startTrainedModelDeployment(...p: Parameters) { + await modelIdsCheck(p); return mlClient.startTrainedModelDeployment(...p); }, async stopTrainedModelDeployment(...p: Parameters) { + await modelIdsCheck(p); return mlClient.stopTrainedModelDeployment(...p); }, async info(...p: Parameters) { @@ -497,7 +550,14 @@ export function getMlClient( return resp; }, async putTrainedModel(...p: Parameters) { - return mlClient.putTrainedModel(...p); + const resp = await mlClient.putTrainedModel(...p); + const [modelId] = getModelIdsFromRequest(p); + if (modelId !== undefined) { + const model = (p[0] as estypes.MlPutTrainedModelRequest).body; + const job = getJobDetailsFromTrainedModel(model); + await jobSavedObjectService.createTrainedModel(modelId, job); + } + return resp; }, async revertModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); @@ -586,6 +646,12 @@ function getDFAJobIdsFromRequest([params]: MlGetDFAParams): string[] { return ids || []; } +function getModelIdsFromRequest([params]: MlGetTrainedModelParams): string[] { + const id = params?.model_id; + const ids = Array.isArray(id) ? id : id?.split(','); + return ids || []; +} + function getADJobIdsFromRequest([params]: MlGetADParams): string[] { const ids = typeof params?.job_id === 'string' ? params?.job_id.split(',') : params?.job_id; return ids || []; @@ -601,3 +667,11 @@ function getJobIdFromBody(p: any): string | undefined { const [params] = p; return params?.body?.job_id; } + +function filterAll(ids: string[]) { + // if _all has been passed as the only id, remove it and assume it was + // an empty list, so all items are returned. + // if _all is one of many ids, the endpoint should look for + // something called _all, which will subsequently fail. + return ids.length === 1 && ids[0] === '_all' ? [] : ids; +} diff --git a/x-pack/plugins/ml/server/lib/ml_client/types.ts b/x-pack/plugins/ml/server/lib/ml_client/types.ts index b4778f4e6d5b1..7a2a565e5b04a 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/types.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/types.ts @@ -86,3 +86,11 @@ export type MlGetDFAParams = | Parameters | Parameters | Parameters; + +export type MlGetTrainedModelParams = + | Parameters + | Parameters + | Parameters + | Parameters + | Parameters + | Parameters; diff --git a/x-pack/plugins/ml/server/lib/route_guard.ts b/x-pack/plugins/ml/server/lib/route_guard.ts index 6614affdf0ea7..0b445eeeae396 100644 --- a/x-pack/plugins/ml/server/lib/route_guard.ts +++ b/x-pack/plugins/ml/server/lib/route_guard.ts @@ -101,6 +101,7 @@ export class RouteGuard { internalSavedObjectsClient, this._spacesPlugin !== undefined, this._authorization, + client, this._isMlReady ); diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts index c12f611c011f6..714d02704ff92 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts @@ -94,7 +94,6 @@ export function modelsProvider( } const { trained_model_stats: trainedModelStats } = await mlClient.getTrainedModelsStats({ - model_id: '_all', size: 10000, }); diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 6ce9a8d93be26..8c59c0f9d519b 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -62,6 +62,7 @@ import { ML_ALERT_TYPES } from '../common/constants/alerts'; import { alertingRoutes } from './routes/alerting'; import { registerCollector } from './usage'; import { FieldFormatsStart } from '../../../../src/plugins/field_formats/server'; +import { SavedObjectsSyncService } from './saved_objects/sync_task'; export type MlPluginSetup = SharedServices; export type MlPluginStart = void; @@ -81,11 +82,13 @@ export class MlServerPlugin private dataViews: DataViewsPluginStart | null = null; private isMlReady: Promise; private setMlReady: () => void = () => {}; + private savedObjectsSyncService: SavedObjectsSyncService; constructor(ctx: PluginInitializerContext) { this.log = ctx.logger.get(); this.mlLicense = new MlLicense(); this.isMlReady = new Promise((resolve) => (this.setMlReady = resolve)); + this.savedObjectsSyncService = new SavedObjectsSyncService(this.log); } public setup(coreSetup: CoreSetup, plugins: PluginsSetup): MlPluginSetup { @@ -141,6 +144,12 @@ export class MlServerPlugin // initialize capabilities switcher to add license filter to ml capabilities setupCapabilitiesSwitcher(coreSetup, plugins.licensing.license$, this.log); setupSavedObjects(coreSetup.savedObjects); + this.savedObjectsSyncService.registerSyncTask( + plugins.taskManager, + plugins.security, + this.spacesPlugin !== undefined, + () => this.isMlReady + ); const { getInternalSavedObjectsClient, getMlSavedObjectsClient } = savedObjectClientsFactory( () => this.savedObjectsStart @@ -255,6 +264,7 @@ export class MlServerPlugin initializeJobs().finally(() => { this.setMlReady(); }); + this.savedObjectsSyncService.scheduleSyncTask(plugins.taskManager, coreStart); } public stop() { diff --git a/x-pack/plugins/ml/server/routes/apidoc.json b/x-pack/plugins/ml/server/routes/apidoc.json index 29edb6106a993..cf1dd61a95930 100644 --- a/x-pack/plugins/ml/server/routes/apidoc.json +++ b/x-pack/plugins/ml/server/routes/apidoc.json @@ -154,17 +154,21 @@ "InitializeJobSavedObjects", "SyncCheck", "UpdateJobsSpaces", + "updateTrainedModelsSpaces", "RemoveJobsFromCurrentSpace", "JobsSpaces", + "TrainedModelsSpaces", "CanDeleteJob", "TrainedModels", "GetTrainedModel", "GetTrainedModelStats", + "GetTrainedModelStatsById", "GetTrainedModelsNodesOverview", "GetTrainedModelPipelines", "StartTrainedModelDeployment", "StopTrainedModelDeployment", + "PutTrainedModel", "DeleteTrainedModel", "Alerting", diff --git a/x-pack/plugins/ml/server/routes/saved_objects.ts b/x-pack/plugins/ml/server/routes/saved_objects.ts index fded5500824b3..b3f7818389bee 100644 --- a/x-pack/plugins/ml/server/routes/saved_objects.ts +++ b/x-pack/plugins/ml/server/routes/saved_objects.ts @@ -7,9 +7,10 @@ import { wrapError } from '../client/error_wrapper'; import { RouteInitialization, SavedObjectsRouteDeps } from '../types'; -import { checksFactory, syncSavedObjectsFactory } from '../saved_objects'; +import { checksFactory, syncSavedObjectsFactory, JobSavedObjectStatus } from '../saved_objects'; import { - jobsAndSpaces, + updateJobsSpaces, + updateTrainedModelsSpaces, jobsAndCurrentSpace, syncJobObjects, syncCheckSchema, @@ -17,7 +18,7 @@ import { jobTypeSchema, } from './schemas/saved_objects'; import { spacesUtilsProvider } from '../lib/spaces_utils'; -import { JobType } from '../../common/types/saved_objects'; +import type { JobType, TrainedModelType } from '../../common/types/saved_objects'; /** * Routes for job saved object management @@ -39,7 +40,7 @@ export function savedObjectsRoutes( path: '/api/ml/saved_objects/status', validate: false, options: { - tags: ['access:ml:canGetJobs'], + tags: ['access:ml:canGetJobs', 'access:ml:canGetTrainedModels'], }, }, routeGuard.fullLicenseAPIGuard(async ({ client, response, jobSavedObjectService }) => { @@ -74,7 +75,11 @@ export function savedObjectsRoutes( query: syncJobObjects, }, options: { - tags: ['access:ml:canCreateJob', 'access:ml:canCreateDataFrameAnalytics'], + tags: [ + 'access:ml:canCreateJob', + 'access:ml:canCreateDataFrameAnalytics', + 'access:ml:canCreateTrainedModels', + ], }, }, routeGuard.fullLicenseAPIGuard(async ({ client, request, response, jobSavedObjectService }) => { @@ -107,7 +112,11 @@ export function savedObjectsRoutes( query: syncJobObjects, }, options: { - tags: ['access:ml:canCreateJob', 'access:ml:canCreateDataFrameAnalytics'], + tags: [ + 'access:ml:canCreateJob', + 'access:ml:canCreateDataFrameAnalytics', + 'access:ml:canCreateTrainedModels', + ], }, }, routeGuard.fullLicenseAPIGuard(async ({ client, request, response, jobSavedObjectService }) => { @@ -140,14 +149,18 @@ export function savedObjectsRoutes( body: syncCheckSchema, }, options: { - tags: ['access:ml:canGetJobs', 'access:ml:canGetDataFrameAnalytics'], + tags: [ + 'access:ml:canGetJobs', + 'access:ml:canGetDataFrameAnalytics', + 'access:ml:canGetTrainedModels', + ], }, }, routeGuard.fullLicenseAPIGuard(async ({ client, request, response, jobSavedObjectService }) => { try { const { jobType } = request.body; const { isSyncNeeded } = syncSavedObjectsFactory(client, jobSavedObjectService); - const result = await isSyncNeeded(jobType as JobType); + const result = await isSyncNeeded(jobType as JobType | TrainedModelType); return response.ok({ body: { result }, @@ -163,15 +176,15 @@ export function savedObjectsRoutes( * * @api {post} /api/ml/saved_objects/update_jobs_spaces Update what spaces jobs are assigned to * @apiName UpdateJobsSpaces - * @apiDescription Update a list of jobs to add and/or remove them from given spaces + * @apiDescription Update a list of jobs to add and/or remove them from given spaces. * - * @apiSchema (body) jobsAndSpaces + * @apiSchema (body) updateJobsSpaces */ router.post( { path: '/api/ml/saved_objects/update_jobs_spaces', validate: { - body: jobsAndSpaces, + body: updateJobsSpaces, }, options: { tags: ['access:ml:canCreateJob', 'access:ml:canCreateDataFrameAnalytics'], @@ -197,12 +210,50 @@ export function savedObjectsRoutes( }) ); + /** + * @apiGroup JobSavedObjects + * + * @api {post} /api/ml/saved_objects/update_trained_models_spaces Update what spaces trained models are assigned to + * @apiName UpdateTrainedModelsSpaces + * @apiDescription Update a list of trained models to add and/or remove them from given spaces. + * + * @apiSchema (body) updateTrainedModelsSpaces + */ + router.post( + { + path: '/api/ml/saved_objects/update_trained_models_spaces', + validate: { + body: updateTrainedModelsSpaces, + }, + options: { + tags: ['access:ml:canCreateTrainedModels'], + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ request, response, jobSavedObjectService }) => { + try { + const { modelIds, spacesToAdd, spacesToRemove } = request.body; + + const body = await jobSavedObjectService.updateTrainedModelsSpaces( + modelIds, + spacesToAdd, + spacesToRemove + ); + + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + /** * @apiGroup JobSavedObjects * * @api {post} /api/ml/saved_objects/remove_job_from_current_space Remove jobs from the current space * @apiName RemoveJobsFromCurrentSpace - * @apiDescription Remove a list of jobs from the current space + * @apiDescription Remove a list of jobs from the current space. * * @apiSchema (body) jobsAndCurrentSpace */ @@ -262,15 +313,19 @@ export function savedObjectsRoutes( path: '/api/ml/saved_objects/jobs_spaces', validate: false, options: { - tags: ['access:ml:canGetJobs'], + tags: ['access:ml:canGetJobs', 'access:ml:canGetDataFrameAnalytics'], }, }, routeGuard.fullLicenseAPIGuard(async ({ response, jobSavedObjectService, client }) => { try { const { checkStatus } = checksFactory(client, jobSavedObjectService); - const allStatuses = Object.values((await checkStatus()).savedObjects).flat(); - - const body = allStatuses + const savedObjects = (await checkStatus()).savedObjects; + const jobStatus = ( + Object.entries(savedObjects) + .filter(([type]) => type === 'anomaly-detector' || type === 'data-frame-analytics') + .map(([, status]) => status) + .flat() as JobSavedObjectStatus[] + ) .filter((s) => s.checks.jobExists) .reduce((acc, cur) => { const type = cur.type; @@ -282,7 +337,46 @@ export function savedObjectsRoutes( }, {} as { [id: string]: { [id: string]: string[] | undefined } }); return response.ok({ - body, + body: jobStatus, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup JobSavedObjects + * + * @api {get} /api/ml/saved_objects/trained_models_spaces Get all trained models and their spaces + * @apiName TrainedModelsSpaces + * @apiDescription List all trained models and their spaces. + * + */ + router.get( + { + path: '/api/ml/saved_objects/trained_models_spaces', + validate: false, + options: { + tags: ['access:ml:canGetTrainedModels'], + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ response, jobSavedObjectService, client }) => { + try { + const { checkStatus } = checksFactory(client, jobSavedObjectService); + const savedObjects = (await checkStatus()).savedObjects; + const modelStatus = savedObjects['trained-model'] + .filter((s) => s.checks.trainedModelExists) + .reduce( + (acc, cur) => { + acc.trainedModels[cur.modelId] = cur.namespaces; + return acc; + }, + { trainedModels: {} } as { trainedModels: { [id: string]: string[] | undefined } } + ); + + return response.ok({ + body: modelStatus, }); } catch (e) { return response.customError(wrapError(e)); diff --git a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts index 062dbd41436d7..941edb31c79fa 100644 --- a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts @@ -26,3 +26,7 @@ export const getInferenceQuerySchema = schema.object({ with_pipelines: schema.maybe(schema.string()), include: schema.maybe(schema.string()), }); + +export const putTrainedModelQuerySchema = schema.object({ + defer_definition_decompression: schema.maybe(schema.boolean()), +}); diff --git a/x-pack/plugins/ml/server/routes/schemas/saved_objects.ts b/x-pack/plugins/ml/server/routes/schemas/saved_objects.ts index 5a62392b5f5eb..7fb229d80a2be 100644 --- a/x-pack/plugins/ml/server/routes/schemas/saved_objects.ts +++ b/x-pack/plugins/ml/server/routes/schemas/saved_objects.ts @@ -14,13 +14,19 @@ export const jobTypeLiterals = schema.oneOf([ export const jobTypeSchema = schema.object({ jobType: jobTypeLiterals }); -export const jobsAndSpaces = schema.object({ +export const updateJobsSpaces = schema.object({ jobType: jobTypeLiterals, jobIds: schema.arrayOf(schema.string()), spacesToAdd: schema.arrayOf(schema.string()), spacesToRemove: schema.arrayOf(schema.string()), }); +export const updateTrainedModelsSpaces = schema.object({ + modelIds: schema.arrayOf(schema.string()), + spacesToAdd: schema.arrayOf(schema.string()), + spacesToRemove: schema.arrayOf(schema.string()), +}); + export const jobsAndCurrentSpace = schema.object({ jobType: jobTypeLiterals, jobIds: schema.arrayOf(schema.string()), diff --git a/x-pack/plugins/ml/server/routes/trained_models.ts b/x-pack/plugins/ml/server/routes/trained_models.ts index 72b93ba45a104..2cbf9a4dde763 100644 --- a/x-pack/plugins/ml/server/routes/trained_models.ts +++ b/x-pack/plugins/ml/server/routes/trained_models.ts @@ -5,12 +5,14 @@ * 2.0. */ +import { schema } from '@kbn/config-schema'; import { RouteInitialization } from '../types'; import { wrapError } from '../client/error_wrapper'; import { getInferenceQuerySchema, modelIdSchema, optionalModelIdSchema, + putTrainedModelQuerySchema, } from './schemas/inference_schema'; import { modelsProvider } from '../models/data_frame_analytics'; import { TrainedModelConfigResponse } from '../../common/types/trained_models'; @@ -34,7 +36,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) query: getInferenceQuerySchema, }, options: { - tags: ['access:ml:canGetDataFrameAnalytics'], + tags: ['access:ml:canGetTrainedModels'], }, }, routeGuard.fullLicenseAPIGuard(async ({ client, mlClient, request, response }) => { @@ -79,7 +81,9 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) } } catch (e) { // the user might not have required permissions to fetch pipelines - mlLog.error(e); + // log the error to the debug log as this might be a common situation and + // we don't need to fill kibana's log with these messages. + mlLog.debug(e); } return response.ok({ @@ -94,8 +98,35 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) /** * @apiGroup TrainedModels * - * @api {get} /api/ml/trained_models/:modelId/_stats Get stats of a trained model + * @api {get} /api/ml/trained_models/_stats Get stats for all trained models * @apiName GetTrainedModelStats + * @apiDescription Retrieves usage information for all trained models. + */ + router.get( + { + path: '/api/ml/trained_models/_stats', + validate: false, + options: { + tags: ['access:ml:canGetTrainedModels'], + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { + try { + const body = await mlClient.getTrainedModelsStats(); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup TrainedModels + * + * @api {get} /api/ml/trained_models/:modelId/_stats Get stats of a trained model + * @apiName GetTrainedModelStatsById * @apiDescription Retrieves usage information for trained models. */ router.get( @@ -105,7 +136,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) params: modelIdSchema, }, options: { - tags: ['access:ml:canGetDataFrameAnalytics'], + tags: ['access:ml:canGetTrainedModels'], }, }, routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { @@ -137,7 +168,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) params: modelIdSchema, }, options: { - tags: ['access:ml:canGetDataFrameAnalytics'], + tags: ['access:ml:canGetTrainedModels'], }, }, routeGuard.fullLicenseAPIGuard(async ({ client, request, mlClient, response }) => { @@ -155,6 +186,44 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) }) ); + /** + * @apiGroup TrainedModels + * + * @api {put} /api/ml/trained_models/:modelId Put a trained model + * @apiName PutTrainedModel + * @apiDescription Adds a new trained model + */ + router.put( + { + path: '/api/ml/trained_models/{modelId}', + validate: { + params: modelIdSchema, + body: schema.any(), + query: putTrainedModelQuerySchema, + }, + options: { + tags: ['access:ml:canCreateTrainedModels'], + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { + try { + const { modelId } = request.params; + const body = await mlClient.putTrainedModel({ + model_id: modelId, + body: request.body, + ...(request.query?.defer_definition_decompression + ? { defer_definition_decompression: true } + : {}), + }); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + /** * @apiGroup TrainedModels * @@ -169,7 +238,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) params: modelIdSchema, }, options: { - tags: ['access:ml:canDeleteDataFrameAnalytics'], + tags: ['access:ml:canDeleteTrainedModels'], }, }, routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { @@ -203,6 +272,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) 'access:ml:canViewMlNodes', 'access:ml:canGetDataFrameAnalytics', 'access:ml:canGetJobs', + 'access:ml:canGetTrainedModels', ], }, }, @@ -237,7 +307,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) params: modelIdSchema, }, options: { - tags: ['access:ml:canGetDataFrameAnalytics'], + tags: ['access:ml:canStartStopTrainedModels'], }, }, routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { @@ -270,7 +340,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) query: forceQuerySchema, }, options: { - tags: ['access:ml:canGetDataFrameAnalytics'], + tags: ['access:ml:canStartStopTrainedModels'], }, }, routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response }) => { diff --git a/x-pack/plugins/ml/server/saved_objects/authorization.ts b/x-pack/plugins/ml/server/saved_objects/authorization.ts index a012fec8029c6..67109cbe22966 100644 --- a/x-pack/plugins/ml/server/saved_objects/authorization.ts +++ b/x-pack/plugins/ml/server/saved_objects/authorization.ts @@ -7,7 +7,7 @@ import { KibanaRequest } from 'kibana/server'; import type { SecurityPluginSetup } from '../../../security/server'; -import { ML_SAVED_OBJECT_TYPE } from '../../common/types/saved_objects'; +import { ML_JOB_SAVED_OBJECT_TYPE } from '../../common/types/saved_objects'; export function authorizationProvider(authorization: SecurityPluginSetup['authz']) { async function authorizationCheck(request: KibanaRequest) { @@ -28,7 +28,7 @@ export function authorizationProvider(authorization: SecurityPluginSetup['authz' const checkPrivilegesDynamicallyWithRequest = authorization.checkPrivilegesDynamicallyWithRequest(request); const createMLJobAuthorizationAction = authorization.actions.savedObject.get( - ML_SAVED_OBJECT_TYPE, + ML_JOB_SAVED_OBJECT_TYPE, 'create' ); const canCreateGlobally = ( diff --git a/x-pack/plugins/ml/server/saved_objects/checks.ts b/x-pack/plugins/ml/server/saved_objects/checks.ts index f4085d99ad636..efb1dce8eac28 100644 --- a/x-pack/plugins/ml/server/saved_objects/checks.ts +++ b/x-pack/plugins/ml/server/saved_objects/checks.ts @@ -6,14 +6,16 @@ */ import Boom from '@hapi/boom'; -import { IScopedClusterClient, KibanaRequest } from 'kibana/server'; -import type { JobSavedObjectService } from './service'; -import { JobType, DeleteJobCheckResponse } from '../../common/types/saved_objects'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { IScopedClusterClient, KibanaRequest } from 'kibana/server'; +import type { JobSavedObjectService, TrainedModelJob } from './service'; +import type { JobType, DeleteJobCheckResponse } from '../../common/types/saved_objects'; -import { DataFrameAnalyticsConfig } from '../../common/types/data_frame_analytics'; -import { ResolveMlCapabilities } from '../../common/types/capabilities'; +import type { DataFrameAnalyticsConfig } from '../../common/types/data_frame_analytics'; +import type { ResolveMlCapabilities } from '../../common/types/capabilities'; +import { getJobDetailsFromTrainedModel } from './util'; -interface JobSavedObjectStatus { +export interface JobSavedObjectStatus { jobId: string; type: JobType; datafeedId?: string | null; @@ -24,6 +26,16 @@ interface JobSavedObjectStatus { }; } +export interface TrainedModelSavedObjectStatus { + modelId: string; + namespaces: string[] | undefined; + job: null | TrainedModelJob; + checks: { + trainedModelExists: boolean; + dfaJobExists: boolean | null; + }; +} + export interface JobStatus { jobId: string; datafeedId?: string | null; @@ -32,12 +44,24 @@ export interface JobStatus { }; } +export interface TrainedModelStatus { + modelId: string; + checks: { + savedObjectExits: boolean; + dfaJobReferenced: boolean | null; + }; +} + export interface StatusResponse { savedObjects: { - [type in JobType]: JobSavedObjectStatus[]; + 'anomaly-detector': JobSavedObjectStatus[]; + 'data-frame-analytics': JobSavedObjectStatus[]; + 'trained-model': TrainedModelSavedObjectStatus[]; }; jobs: { - [type in JobType]: JobStatus[]; + 'anomaly-detector': JobStatus[]; + 'data-frame-analytics': JobStatus[]; + 'trained-model': TrainedModelStatus[]; }; } @@ -46,16 +70,29 @@ export function checksFactory( jobSavedObjectService: JobSavedObjectService ) { async function checkStatus(): Promise { - const jobObjects = await jobSavedObjectService.getAllJobObjects(undefined, false); - - // load all non-space jobs and datafeeds - const adJobs = await client.asInternalUser.ml.getJobs(); - const datafeeds = await client.asInternalUser.ml.getDatafeeds(); - const dfaJobs = (await client.asInternalUser.ml.getDataFrameAnalytics()) as unknown as { - data_frame_analytics: DataFrameAnalyticsConfig[]; - }; + const [ + jobObjects, + allJobObjects, + modelObjects, + allModelObjects, + adJobs, + datafeeds, + dfaJobs, + models, + ] = await Promise.all([ + jobSavedObjectService.getAllJobObjects(undefined, false), + jobSavedObjectService.getAllJobObjectsForAllSpaces(), + jobSavedObjectService.getAllTrainedModelObjects(false), + jobSavedObjectService.getAllTrainedModelObjectsForAllSpaces(), + client.asInternalUser.ml.getJobs(), + client.asInternalUser.ml.getDatafeeds(), + client.asInternalUser.ml.getDataFrameAnalytics() as unknown as { + data_frame_analytics: DataFrameAnalyticsConfig[]; + }, + client.asInternalUser.ml.getTrainedModels(), + ]); - const savedObjectsStatus: JobSavedObjectStatus[] = jobObjects.map( + const jobSavedObjectsStatus: JobSavedObjectStatus[] = jobObjects.map( ({ attributes, namespaces }) => { const type: JobType = attributes.type; const jobId = attributes.job_id; @@ -84,7 +121,42 @@ export function checksFactory( } ); - const allJobObjects = await jobSavedObjectService.getAllJobObjectsForAllSpaces(); + const dfaJobsCreateTimeMap = dfaJobs.data_frame_analytics.reduce((acc, cur) => { + acc.set(cur.id, cur.create_time); + return acc; + }, new Map()); + + const modelJobExits = models.trained_model_configs.reduce((acc, cur) => { + const job = getJobDetailsFromTrainedModel(cur); + if (job === null) { + return acc; + } + + const { job_id: jobId, create_time: createTime } = job; + const exists = createTime === dfaJobsCreateTimeMap.get(jobId); + + if (jobId && createTime) { + acc.set(cur.model_id, exists); + } + return acc; + }, new Map()); + + const modelSavedObjectsStatus: TrainedModelSavedObjectStatus[] = modelObjects.map( + ({ attributes: { job, model_id: modelId }, namespaces }) => { + const trainedModelExists = models.trained_model_configs.some((m) => m.model_id === modelId); + const dfaJobExists = modelJobExits.get(modelId) ?? null; + + return { + modelId, + namespaces, + job, + checks: { + trainedModelExists, + dfaJobExists, + }, + }; + } + ); const nonSpaceADObjectIds = new Set( allJobObjects @@ -97,16 +169,23 @@ export function checksFactory( .map(({ attributes }) => attributes.job_id) ); + const nonSpaceModelObjectIds = new Map( + allModelObjects.map((model) => [model.attributes.model_id, model]) + ); + const adObjectIds = new Set( - savedObjectsStatus.filter(({ type }) => type === 'anomaly-detector').map(({ jobId }) => jobId) + jobSavedObjectsStatus + .filter(({ type }) => type === 'anomaly-detector') + .map(({ jobId }) => jobId) ); const dfaObjectIds = new Set( - savedObjectsStatus + jobSavedObjectsStatus .filter(({ type }) => type === 'data-frame-analytics') .map(({ jobId }) => jobId) ); + const modelObjectIds = new Set(modelSavedObjectsStatus.map(({ modelId }) => modelId)); - const anomalyDetectors = adJobs.jobs + const anomalyDetectorsStatus = adJobs.jobs .filter(({ job_id: jobId }) => { // only list jobs which are in the current space (adObjectIds) // or are not in any spaces (nonSpaceADObjectIds) @@ -123,7 +202,7 @@ export function checksFactory( }; }); - const dataFrameAnalytics = dfaJobs.data_frame_analytics + const dataFrameAnalyticsStatus = dfaJobs.data_frame_analytics .filter(({ id: jobId }) => { // only list jobs which are in the current space (dfaObjectIds) // or are not in any spaces (nonSpaceDFAObjectIds) @@ -139,16 +218,47 @@ export function checksFactory( }; }); + const modelsStatus = models.trained_model_configs + .filter(({ model_id: modelId }) => { + // only list jobs which are in the current space (adObjectIds) + // or are not in any spaces (nonSpaceADObjectIds) + return ( + modelObjectIds.has(modelId) === true || nonSpaceModelObjectIds.has(modelId) === false + ); + }) + .map((model: estypes.MlTrainedModelConfig) => { + const modelId = model.model_id; + const modelObject = nonSpaceModelObjectIds.get(modelId); + const savedObjectExits = modelObject !== undefined; + const job = getJobDetailsFromTrainedModel(model); + let dfaJobReferenced = null; + if (job !== null) { + dfaJobReferenced = + modelObject?.attributes.job?.job_id === job.job_id && + modelObject?.attributes.job?.create_time === job.create_time; + } + + return { + modelId, + checks: { + savedObjectExits, + dfaJobReferenced, + }, + }; + }); + return { savedObjects: { - 'anomaly-detector': savedObjectsStatus.filter(({ type }) => type === 'anomaly-detector'), - 'data-frame-analytics': savedObjectsStatus.filter( + 'anomaly-detector': jobSavedObjectsStatus.filter(({ type }) => type === 'anomaly-detector'), + 'data-frame-analytics': jobSavedObjectsStatus.filter( ({ type }) => type === 'data-frame-analytics' ), + 'trained-model': modelSavedObjectsStatus, }, jobs: { - 'anomaly-detector': anomalyDetectors, - 'data-frame-analytics': dataFrameAnalytics, + 'anomaly-detector': anomalyDetectorsStatus, + 'data-frame-analytics': dataFrameAnalyticsStatus, + 'trained-model': modelsStatus, }, }; } diff --git a/x-pack/plugins/ml/server/saved_objects/index.ts b/x-pack/plugins/ml/server/saved_objects/index.ts index 652f47122ae2f..7537c7ed01dcc 100644 --- a/x-pack/plugins/ml/server/saved_objects/index.ts +++ b/x-pack/plugins/ml/server/saved_objects/index.ts @@ -9,6 +9,7 @@ export { setupSavedObjects } from './saved_objects'; export type { JobObject, JobSavedObjectService } from './service'; export { jobSavedObjectServiceFactory } from './service'; export { checksFactory } from './checks'; +export type { JobSavedObjectStatus } from './checks'; export { syncSavedObjectsFactory } from './sync'; export { jobSavedObjectsInitializationFactory } from './initialization'; export { savedObjectClientsFactory } from './util'; diff --git a/x-pack/plugins/ml/server/saved_objects/initialization/initialization.ts b/x-pack/plugins/ml/server/saved_objects/initialization/initialization.ts index c0ab8600794c6..1764c59f7e446 100644 --- a/x-pack/plugins/ml/server/saved_objects/initialization/initialization.ts +++ b/x-pack/plugins/ml/server/saved_objects/initialization/initialization.ts @@ -10,7 +10,7 @@ import { savedObjectClientsFactory } from '../util'; import { syncSavedObjectsFactory } from '../sync'; import { jobSavedObjectServiceFactory, JobObject } from '../service'; import { mlLog } from '../../lib/log'; -import { ML_SAVED_OBJECT_TYPE } from '../../../common/types/saved_objects'; +import { ML_JOB_SAVED_OBJECT_TYPE } from '../../../common/types/saved_objects'; import { createJobSpaceOverrides } from './space_overrides'; import type { SecurityPluginSetup } from '../../../../security/server'; @@ -52,18 +52,19 @@ export function jobSavedObjectsInitializationFactory( savedObjectsClient, spacesEnabled, security?.authz, + client, () => Promise.resolve() // pretend isMlReady, to allow us to initialize the saved objects ); - mlLog.info('Initializing job saved objects'); + mlLog.info('Initializing ML saved objects'); // create space overrides for specific jobs const jobSpaceOverrides = await createJobSpaceOverrides(client); // initialize jobs const { initSavedObjects } = syncSavedObjectsFactory(client, jobSavedObjectService); - const { jobs } = await initSavedObjects(false, jobSpaceOverrides); - mlLog.info(`${jobs.length} job saved objects initialized`); + const { jobs, trainedModels } = await initSavedObjects(false, jobSpaceOverrides); + mlLog.info(`${jobs.length + trainedModels.length} ML saved objects initialized`); } catch (error) { - mlLog.error(`Error Initializing jobs ${JSON.stringify(error)}`); + mlLog.error(`Error Initializing ML saved objects ${JSON.stringify(error)}`); } } @@ -88,7 +89,7 @@ export function jobSavedObjectsInitializationFactory( async function _jobSavedObjectsExist(savedObjectsClient: SavedObjectsClientContract) { const options = { - type: ML_SAVED_OBJECT_TYPE, + type: ML_JOB_SAVED_OBJECT_TYPE, perPage: 0, namespaces: ['*'], }; diff --git a/x-pack/plugins/ml/server/saved_objects/mappings.ts b/x-pack/plugins/ml/server/saved_objects/mappings.ts index f452991015723..7560df4b6ab22 100644 --- a/x-pack/plugins/ml/server/saved_objects/mappings.ts +++ b/x-pack/plugins/ml/server/saved_objects/mappings.ts @@ -31,6 +31,34 @@ export const mlJob: SavedObjectsTypeMappingDefinition = { }, }; +export const mlTrainedModel: SavedObjectsTypeMappingDefinition = { + properties: { + model_id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + job: { + properties: { + job_id: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + create_time: { + type: 'date', + }, + }, + }, + }, +}; + export const mlModule: SavedObjectsTypeMappingDefinition = { dynamic: false, properties: { diff --git a/x-pack/plugins/ml/server/saved_objects/saved_objects.ts b/x-pack/plugins/ml/server/saved_objects/saved_objects.ts index f3061d3e38d7a..2fdcd58416e6a 100644 --- a/x-pack/plugins/ml/server/saved_objects/saved_objects.ts +++ b/x-pack/plugins/ml/server/saved_objects/saved_objects.ts @@ -6,22 +6,30 @@ */ import { SavedObjectsServiceSetup } from 'kibana/server'; -import { mlJob, mlModule } from './mappings'; +import { mlJob, mlTrainedModel, mlModule } from './mappings'; import { migrations } from './migrations'; import { - ML_SAVED_OBJECT_TYPE, + ML_JOB_SAVED_OBJECT_TYPE, ML_MODULE_SAVED_OBJECT_TYPE, + ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, } from '../../common/types/saved_objects'; export function setupSavedObjects(savedObjects: SavedObjectsServiceSetup) { savedObjects.registerType({ - name: ML_SAVED_OBJECT_TYPE, + name: ML_JOB_SAVED_OBJECT_TYPE, hidden: false, namespaceType: 'multiple', migrations, mappings: mlJob, }); + savedObjects.registerType({ + name: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + hidden: false, + namespaceType: 'multiple', + migrations, + mappings: mlTrainedModel, + }); savedObjects.registerType({ name: ML_MODULE_SAVED_OBJECT_TYPE, hidden: false, diff --git a/x-pack/plugins/ml/server/saved_objects/service.ts b/x-pack/plugins/ml/server/saved_objects/service.ts index ff8856457ca4f..6e3b9a2485c8a 100644 --- a/x-pack/plugins/ml/server/saved_objects/service.ts +++ b/x-pack/plugins/ml/server/saved_objects/service.ts @@ -11,10 +11,15 @@ import { SavedObjectsClientContract, SavedObjectsFindOptions, SavedObjectsFindResult, + IScopedClusterClient, } from 'kibana/server'; import type { SecurityPluginSetup } from '../../../security/server'; -import { JobType, ML_SAVED_OBJECT_TYPE } from '../../common/types/saved_objects'; -import { MLJobNotFound } from '../lib/ml_client'; +import type { JobType, MlSavedObjectType } from '../../common/types/saved_objects'; +import { + ML_JOB_SAVED_OBJECT_TYPE, + ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, +} from '../../common/types/saved_objects'; +import { MLJobNotFound, MLModelNotFound } from '../lib/ml_client'; import { getSavedObjectClientError } from './util'; import { authorizationProvider } from './authorization'; @@ -25,13 +30,31 @@ export interface JobObject { } type JobObjectFilter = { [k in keyof JobObject]?: string }; +export interface TrainedModelObject { + model_id: string; + job: null | TrainedModelJob; +} + +export interface TrainedModelJob { + job_id: string; + create_time: number; +} + +type TrainedModelObjectFilter = { [k in keyof TrainedModelObject]?: string }; + export type JobSavedObjectService = ReturnType; +type UpdateJobsSpacesResult = Record< + string, + { success: boolean; type: MlSavedObjectType; error?: any } +>; + export function jobSavedObjectServiceFactory( savedObjectsClient: SavedObjectsClientContract, internalSavedObjectsClient: SavedObjectsClientContract, spacesEnabled: boolean, authorization: SecurityPluginSetup['authz'] | undefined, + client: IScopedClusterClient, isMlReady: () => Promise ) { async function _getJobObjects( @@ -51,10 +74,13 @@ export function jobSavedObjectServiceFactory( } else if (datafeedId !== undefined) { filterObject.datafeed_id = datafeedId; } - const { filter, searchFields } = createSavedObjectFilter(filterObject); + const { filter, searchFields } = createSavedObjectFilter( + filterObject, + ML_JOB_SAVED_OBJECT_TYPE + ); const options: SavedObjectsFindOptions = { - type: ML_SAVED_OBJECT_TYPE, + type: ML_JOB_SAVED_OBJECT_TYPE, perPage: 10000, ...(spacesEnabled === false || currentSpaceOnly === true ? {} : { namespaces: ['*'] }), searchFields, @@ -75,7 +101,7 @@ export function jobSavedObjectServiceFactory( type: jobType, }; - const id = savedObjectId(job); + const id = _jobSavedObjectId(job); try { const [existingJobObject] = await getAllJobObjectsForAllSpaces(jobType, jobId); @@ -86,7 +112,7 @@ export function jobSavedObjectServiceFactory( await _forceDeleteJob(jobType, jobId, existingJobObject.namespaces[0]); } else { // the saved object has no spaces, this is unexpected, attempt a normal delete - await savedObjectsClient.delete(ML_SAVED_OBJECT_TYPE, id, { force: true }); + await savedObjectsClient.delete(ML_JOB_SAVED_OBJECT_TYPE, id, { force: true }); } } } catch (error) { @@ -94,7 +120,7 @@ export function jobSavedObjectServiceFactory( // if not, this error will be throw which we ignore. } - await savedObjectsClient.create(ML_SAVED_OBJECT_TYPE, job, { + await savedObjectsClient.create(ML_JOB_SAVED_OBJECT_TYPE, job, { id, }); } @@ -103,15 +129,15 @@ export function jobSavedObjectServiceFactory( await isMlReady(); return await savedObjectsClient.bulkCreate( jobs.map((j) => ({ - type: ML_SAVED_OBJECT_TYPE, - id: savedObjectId(j.job), + type: ML_JOB_SAVED_OBJECT_TYPE, + id: _jobSavedObjectId(j.job), attributes: j.job, initialNamespaces: j.namespaces, })) ); } - function savedObjectId(job: JobObject) { + function _jobSavedObjectId(job: JobObject) { return `${job.type}-${job.job_id}`; } @@ -122,11 +148,11 @@ export function jobSavedObjectServiceFactory( throw new MLJobNotFound('job not found'); } - await savedObjectsClient.delete(ML_SAVED_OBJECT_TYPE, job.id, { force: true }); + await savedObjectsClient.delete(ML_JOB_SAVED_OBJECT_TYPE, job.id, { force: true }); } async function _forceDeleteJob(jobType: JobType, jobId: string, namespace: string) { - const id = savedObjectId({ + const id = _jobSavedObjectId({ job_id: jobId, datafeed_id: null, type: jobType, @@ -134,7 +160,7 @@ export function jobSavedObjectServiceFactory( // * space cannot be used in a delete call, so use undefined which // is the same as specifying the default space - await internalSavedObjectsClient.delete(ML_SAVED_OBJECT_TYPE, id, { + await internalSavedObjectsClient.delete(ML_JOB_SAVED_OBJECT_TYPE, id, { namespace: namespace === '*' ? undefined : namespace, force: true, }); @@ -193,9 +219,12 @@ export function jobSavedObjectServiceFactory( filterObject.job_id = jobId; } - const { filter, searchFields } = createSavedObjectFilter(filterObject); + const { filter, searchFields } = createSavedObjectFilter( + filterObject, + ML_JOB_SAVED_OBJECT_TYPE + ); const options: SavedObjectsFindOptions = { - type: ML_SAVED_OBJECT_TYPE, + type: ML_JOB_SAVED_OBJECT_TYPE, perPage: 10000, ...(spacesEnabled === false ? {} : { namespaces: ['*'] }), searchFields, @@ -214,7 +243,7 @@ export function jobSavedObjectServiceFactory( const jobObject = job.attributes; jobObject.datafeed_id = datafeedId; - await savedObjectsClient.update(ML_SAVED_OBJECT_TYPE, job.id, jobObject); + await savedObjectsClient.update(ML_JOB_SAVED_OBJECT_TYPE, job.id, jobObject); } async function deleteDatafeed(datafeedId: string) { @@ -226,15 +255,15 @@ export function jobSavedObjectServiceFactory( const jobObject = job.attributes; jobObject.datafeed_id = null; - await savedObjectsClient.update(ML_SAVED_OBJECT_TYPE, job.id, jobObject); + await savedObjectsClient.update(ML_JOB_SAVED_OBJECT_TYPE, job.id, jobObject); } - async function getIds(jobType: JobType, idType: keyof JobObject) { + async function _getIds(jobType: JobType, idType: keyof JobObject) { const jobs = await _getJobObjects(jobType); return jobs.map((o) => o.attributes[idType]); } - async function filterJobObjectsForSpace( + async function _filterJobObjectsForSpace( jobType: JobType, list: T[], field: keyof T, @@ -243,12 +272,12 @@ export function jobSavedObjectServiceFactory( if (list.length === 0) { return []; } - const jobIds = await getIds(jobType, key); + const jobIds = await _getIds(jobType, key); return list.filter((j) => jobIds.includes(j[field] as unknown as string)); } async function filterJobsForSpace(jobType: JobType, list: T[], field: keyof T): Promise { - return filterJobObjectsForSpace(jobType, list, field, 'job_id'); + return _filterJobObjectsForSpace(jobType, list, field, 'job_id'); } async function filterDatafeedsForSpace( @@ -256,10 +285,10 @@ export function jobSavedObjectServiceFactory( list: T[], field: keyof T ): Promise { - return filterJobObjectsForSpace(jobType, list, field, 'datafeed_id'); + return _filterJobObjectsForSpace(jobType, list, field, 'datafeed_id'); } - async function filterJobObjectIdsForSpace( + async function _filterJobObjectIdsForSpace( jobType: JobType, ids: string[], key: keyof JobObject, @@ -269,7 +298,7 @@ export function jobSavedObjectServiceFactory( return []; } - const jobIds = await getIds(jobType, key); + const jobIds = await _getIds(jobType, key); // check to see if any of the ids supplied contain a wildcard if (allowWildcards === false || ids.join().match('\\*') === null) { // wildcards are not allowed or no wildcards could be found @@ -291,14 +320,14 @@ export function jobSavedObjectServiceFactory( ids: string[], allowWildcards: boolean = false ): Promise { - return filterJobObjectIdsForSpace(jobType, ids, 'job_id', allowWildcards); + return _filterJobObjectIdsForSpace(jobType, ids, 'job_id', allowWildcards); } async function filterDatafeedIdsForSpace( ids: string[], allowWildcards: boolean = false ): Promise { - return filterJobObjectIdsForSpace('anomaly-detector', ids, 'datafeed_id', allowWildcards); + return _filterJobObjectIdsForSpace('anomaly-detector', ids, 'datafeed_id', allowWildcards); } async function updateJobsSpaces( @@ -306,27 +335,33 @@ export function jobSavedObjectServiceFactory( jobIds: string[], spacesToAdd: string[], spacesToRemove: string[] - ) { - const results: Record = {}; + ): Promise { + if (jobIds.length === 0 || (spacesToAdd.length === 0 && spacesToRemove.length === 0)) { + return {}; + } + + const results: UpdateJobsSpacesResult = {}; const jobs = await _getJobObjects(jobType); const jobObjectIdMap = new Map(); - const objectsToUpdate: Array<{ type: string; id: string }> = []; + const jobObjectsToUpdate: Array<{ type: string; id: string }> = []; + for (const jobId of jobIds) { const job = jobs.find((j) => j.attributes.job_id === jobId); if (job === undefined) { results[jobId] = { success: false, - error: createError(jobId, 'job_id'), + type: ML_JOB_SAVED_OBJECT_TYPE, + error: createJobError(jobId, 'job_id'), }; } else { jobObjectIdMap.set(job.id, jobId); - objectsToUpdate.push({ type: ML_SAVED_OBJECT_TYPE, id: job.id }); + jobObjectsToUpdate.push({ type: ML_JOB_SAVED_OBJECT_TYPE, id: job.id }); } } try { const updateResult = await savedObjectsClient.updateObjectsSpaces( - objectsToUpdate, + jobObjectsToUpdate, spacesToAdd, spacesToRemove ); @@ -335,27 +370,30 @@ export function jobSavedObjectServiceFactory( if (error) { results[jobId] = { success: false, + type: ML_JOB_SAVED_OBJECT_TYPE, error: getSavedObjectClientError(error), }; } else { results[jobId] = { success: true, + type: ML_JOB_SAVED_OBJECT_TYPE, }; } }); } catch (error) { // If the entire operation failed, return success: false for each job const clientError = getSavedObjectClientError(error); - objectsToUpdate.forEach(({ id: objectId }) => { + jobObjectsToUpdate.forEach(({ id: objectId }) => { const jobId = jobObjectIdMap.get(objectId)!; results[jobId] = { success: false, + type: ML_JOB_SAVED_OBJECT_TYPE, error: clientError, }; }); } - return results; + return { ...results }; } async function canCreateGlobalJobs(request: KibanaRequest) { @@ -366,6 +404,335 @@ export function jobSavedObjectServiceFactory( return (await authorizationCheck(request)).canCreateGlobally; } + async function getTrainedModelObject( + modelId: string, + currentSpaceOnly: boolean = true + ): Promise | undefined> { + const [modelObject] = await _getTrainedModelObjects(modelId, currentSpaceOnly); + return modelObject; + } + + async function createTrainedModel(modelId: string, job: TrainedModelJob | null) { + await _createTrainedModel(modelId, job); + } + + async function bulkCreateTrainedModel(models: TrainedModelObject[], namespaceFallback?: string) { + return await _bulkCreateTrainedModel(models, namespaceFallback); + } + + async function deleteTrainedModel(modelId: string) { + await _deleteTrainedModel(modelId); + } + + async function forceDeleteTrainedModel(modelId: string, namespace: string) { + await _forceDeleteTrainedModel(modelId, namespace); + } + + async function getAllTrainedModelObjects(currentSpaceOnly: boolean = true) { + return await _getTrainedModelObjects(undefined, currentSpaceOnly); + } + + async function _getTrainedModelObjects(modelId?: string, currentSpaceOnly: boolean = true) { + await isMlReady(); + const filterObject: TrainedModelObjectFilter = {}; + + if (modelId !== undefined) { + filterObject.model_id = modelId; + } + + const { filter, searchFields } = createSavedObjectFilter( + filterObject, + ML_TRAINED_MODEL_SAVED_OBJECT_TYPE + ); + + const options: SavedObjectsFindOptions = { + type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + perPage: 10000, + ...(spacesEnabled === false || currentSpaceOnly === true ? {} : { namespaces: ['*'] }), + searchFields, + filter, + }; + + const models = await savedObjectsClient.find(options); + + return models.saved_objects; + } + + async function _createTrainedModel(modelId: string, job: TrainedModelJob | null) { + await isMlReady(); + + const modelObject: TrainedModelObject = { + model_id: modelId, + job, + }; + + try { + const [existingModelObject] = await getAllTrainedModelObjectsForAllSpaces([modelId]); + if (existingModelObject !== undefined) { + // a saved object for this job already exists, this may be left over from a previously deleted job + if (existingModelObject.namespaces?.length) { + // use a force delete just in case the saved object exists only in another space. + await _forceDeleteTrainedModel(modelId, existingModelObject.namespaces[0]); + } else { + // the saved object has no spaces, this is unexpected, attempt a normal delete + await savedObjectsClient.delete(ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, modelId, { + force: true, + }); + } + } + } catch (error) { + // the saved object may exist if a previous job with the same ID has been deleted. + // if not, this error will be throw which we ignore. + } + let initialNamespaces; + // if a job exists for this model, ensure the initial namespaces for the model + // are the same as the job + if (job !== null) { + const [existingJobObject] = await getAllJobObjectsForAllSpaces( + 'data-frame-analytics', + job.job_id + ); + + initialNamespaces = existingJobObject?.namespaces ?? undefined; + } + + await savedObjectsClient.create( + ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + modelObject, + { + id: modelId, + ...(initialNamespaces ? { initialNamespaces } : {}), + } + ); + } + + async function _bulkCreateTrainedModel(models: TrainedModelObject[], namespaceFallback?: string) { + await isMlReady(); + + const namespacesPerJob = (await getAllJobObjectsForAllSpaces()).reduce((acc, cur) => { + acc[cur.attributes.job_id] = cur.namespaces; + return acc; + }, {} as Record); + + return await savedObjectsClient.bulkCreate( + models.map((m) => { + let initialNamespaces = m.job && namespacesPerJob[m.job.job_id]; + if (!initialNamespaces?.length && namespaceFallback) { + // use the namespace fallback if it is defined and no namespaces can + // be found for a related job. + // otherwise initialNamespaces will be undefined and the SO client will + // use the current space. + initialNamespaces = [namespaceFallback]; + } + return { + type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + id: m.model_id, + attributes: m, + ...(initialNamespaces ? { initialNamespaces } : {}), + }; + }) + ); + } + + async function getAllTrainedModelObjectsForAllSpaces(modelIds?: string[]) { + await isMlReady(); + const searchFields = ['model_id']; + let filter = ''; + + if (modelIds !== undefined && modelIds.length) { + filter = modelIds + .map((m) => `${ML_TRAINED_MODEL_SAVED_OBJECT_TYPE}.attributes.model_id: "${m}"`) + .join(' OR '); + } + + const options: SavedObjectsFindOptions = { + type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + perPage: 10000, + ...(spacesEnabled === false ? {} : { namespaces: ['*'] }), + searchFields, + filter, + }; + + return (await internalSavedObjectsClient.find(options)).saved_objects; + } + + async function _deleteTrainedModel(modelId: string) { + const [model] = await _getTrainedModelObjects(modelId); + if (model === undefined) { + throw new MLModelNotFound('trained model not found'); + } + + await savedObjectsClient.delete(ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, model.id, { force: true }); + } + + async function _forceDeleteTrainedModel(modelId: string, namespace: string) { + // * space cannot be used in a delete call, so use undefined which + // is the same as specifying the default space + await internalSavedObjectsClient.delete(ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, modelId, { + namespace: namespace === '*' ? undefined : namespace, + force: true, + }); + } + + async function filterTrainedModelsForSpace(list: T[], field: keyof T): Promise { + return _filterModelObjectsForSpace(list, field, 'model_id'); + } + + async function filterTrainedModelIdsForSpace( + ids: string[], + allowWildcards: boolean = false + ): Promise { + return _filterModelObjectIdsForSpace(ids, 'model_id', allowWildcards); + } + + async function _filterModelObjectIdsForSpace( + ids: string[], + key: keyof TrainedModelObject, + allowWildcards: boolean = false + ): Promise { + if (ids.length === 0) { + return []; + } + + const modelIds = await _getModelIds(key); + // check to see if any of the ids supplied contain a wildcard + if (allowWildcards === false || ids.join().match('\\*') === null) { + // wildcards are not allowed or no wildcards could be found + return ids.filter((id) => modelIds.includes(id)); + } + + // if any of the ids contain a wildcard, check each one. + return ids.filter((id) => { + if (id.match('\\*') === null) { + return modelIds.includes(id); + } + const regex = new RE2(id.replace('*', '.*')); + return modelIds.some((jId) => typeof jId === 'string' && regex.exec(jId)); + }); + } + + async function _filterModelObjectsForSpace( + list: T[], + field: keyof T, + key: keyof TrainedModelObject + ): Promise { + if (list.length === 0) { + return []; + } + const modelIds = await _getModelIds(key); + return list.filter((j) => modelIds.includes(j[field] as unknown as string)); + } + + async function _getModelIds(idType: keyof TrainedModelObject) { + const models = await _getTrainedModelObjects(); + return models.map((o) => o.attributes[idType]); + } + + async function findTrainedModelsObjectForJobs( + jobIds: string[], + currentSpaceOnly: boolean = true + ) { + await isMlReady(); + const { data_frame_analytics: jobs } = await client.asInternalUser.ml.getDataFrameAnalytics({ + id: jobIds.join(','), + }); + + const searches = jobs.map((job) => { + const createTime = job.create_time!; + + const filterObject = { + 'job.job_id': job.id, + 'job.create_time': createTime, + } as TrainedModelObjectFilter; + const { filter, searchFields } = createSavedObjectFilter( + filterObject, + ML_TRAINED_MODEL_SAVED_OBJECT_TYPE + ); + + const options: SavedObjectsFindOptions = { + type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + perPage: 10000, + ...(spacesEnabled === false || currentSpaceOnly === true ? {} : { namespaces: ['*'] }), + searchFields, + filter, + }; + return savedObjectsClient.find(options); + }); + + const finedResult = await Promise.all(searches); + return finedResult.reduce((acc, cur) => { + const savedObject = cur.saved_objects[0]; + if (savedObject) { + const jobId = savedObject.attributes.job!.job_id; + acc[jobId] = savedObject; + } + return acc; + }, {} as Record>); + } + + async function updateTrainedModelsSpaces( + modelIds: string[], + spacesToAdd: string[], + spacesToRemove: string[] + ): Promise { + if (modelIds.length === 0 || (spacesToAdd.length === 0 && spacesToRemove.length === 0)) { + return {}; + } + const results: UpdateJobsSpacesResult = {}; + const models = await _getTrainedModelObjects(); + const trainedModelObjectIdMap = new Map(); + const objectsToUpdate: Array<{ type: string; id: string }> = []; + + for (const modelId of modelIds) { + const model = models.find(({ attributes }) => attributes.model_id === modelId); + if (model === undefined) { + results[modelId] = { + success: false, + type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + error: createTrainedModelError(modelId), + }; + } else { + trainedModelObjectIdMap.set(model.id, model.attributes.model_id); + objectsToUpdate.push({ type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, id: model.id }); + } + } + try { + const updateResult = await savedObjectsClient.updateObjectsSpaces( + objectsToUpdate, + spacesToAdd, + spacesToRemove + ); + updateResult.objects.forEach(({ id: objectId, error }) => { + const model = trainedModelObjectIdMap.get(objectId)!; + if (error) { + results[model] = { + success: false, + type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + error: getSavedObjectClientError(error), + }; + } else { + results[model] = { + success: true, + type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + }; + } + }); + } catch (error) { + // If the entire operation failed, return success: false for each job + const clientError = getSavedObjectClientError(error); + objectsToUpdate.forEach(({ id: objectId }) => { + const modelId = trainedModelObjectIdMap.get(objectId)!; + results[modelId] = { + success: false, + type: ML_TRAINED_MODEL_SAVED_OBJECT_TYPE, + error: clientError, + }; + }); + } + + return results; + } + return { getAllJobObjects, getJobObject, @@ -385,10 +752,21 @@ export function jobSavedObjectServiceFactory( bulkCreateJobs, getAllJobObjectsForAllSpaces, canCreateGlobalJobs, + getTrainedModelObject, + createTrainedModel, + bulkCreateTrainedModel, + deleteTrainedModel, + forceDeleteTrainedModel, + updateTrainedModelsSpaces, + getAllTrainedModelObjects, + getAllTrainedModelObjectsForAllSpaces, + filterTrainedModelsForSpace, + filterTrainedModelIdsForSpace, + findTrainedModelsObjectForJobs, }; } -export function createError(id: string, key: keyof JobObject) { +export function createJobError(id: string, key: keyof JobObject) { let reason = `'${id}' not found`; if (key === 'job_id') { reason = `No known job with id '${id}'`; @@ -404,12 +782,24 @@ export function createError(id: string, key: keyof JobObject) { }; } -function createSavedObjectFilter(filterObject: JobObjectFilter) { +export function createTrainedModelError(id: string) { + return { + error: { + reason: `No known trained model with id '${id}'`, + }, + status: 404, + }; +} + +function createSavedObjectFilter( + filterObject: JobObjectFilter | TrainedModelObjectFilter, + savedObjectType: string +) { const searchFields: string[] = []; const filter = Object.entries(filterObject) .map(([k, v]) => { searchFields.push(k); - return `${ML_SAVED_OBJECT_TYPE}.attributes.${k}: "${v}"`; + return `${savedObjectType}.attributes.${k}: "${v}"`; }) .join(' AND '); return { filter, searchFields }; diff --git a/x-pack/plugins/ml/server/saved_objects/sync.ts b/x-pack/plugins/ml/server/saved_objects/sync.ts index d77cdddcfa863..63f62d264a63d 100644 --- a/x-pack/plugins/ml/server/saved_objects/sync.ts +++ b/x-pack/plugins/ml/server/saved_objects/sync.ts @@ -6,16 +6,17 @@ */ import Boom from '@hapi/boom'; -import { IScopedClusterClient } from 'kibana/server'; -import type { JobObject, JobSavedObjectService } from './service'; -import { +import type { IScopedClusterClient } from 'kibana/server'; +import type { JobObject, JobSavedObjectService, TrainedModelObject } from './service'; +import type { JobType, + TrainedModelType, SyncSavedObjectResponse, InitializeSavedObjectResponse, } from '../../common/types/saved_objects'; import { checksFactory } from './checks'; import type { JobStatus } from './checks'; -import { getSavedObjectClientError } from './util'; +import { getSavedObjectClientError, getJobDetailsFromTrainedModel } from './util'; export interface JobSpaceOverrides { overrides: { @@ -37,12 +38,14 @@ export function syncSavedObjectsFactory( datafeedsRemoved: {}, }; - const datafeeds = await client.asInternalUser.ml.getDatafeeds(); + const [datafeeds, models, status] = await Promise.all([ + client.asInternalUser.ml.getDatafeeds(), + client.asInternalUser.ml.getTrainedModels(), + checkStatus(), + ]); const tasks: Array<() => Promise> = []; - const status = await checkStatus(); - const adJobsById = status.jobs['anomaly-detector'].reduce((acc, j) => { acc[j.jobId] = j; return acc; @@ -51,8 +54,11 @@ export function syncSavedObjectsFactory( for (const job of status.jobs['anomaly-detector']) { if (job.checks.savedObjectExits === false) { const type = 'anomaly-detector'; + if (results.savedObjectsCreated[type] === undefined) { + results.savedObjectsCreated[type] = {}; + } if (simulate === true) { - results.savedObjectsCreated[job.jobId] = { success: true, type }; + results.savedObjectsCreated[type]![job.jobId] = { success: true }; } else { // create AD saved objects for jobs which are missing them const jobId = job.jobId; @@ -60,11 +66,11 @@ export function syncSavedObjectsFactory( tasks.push(async () => { try { await jobSavedObjectService.createAnomalyDetectionJob(jobId, datafeedId ?? undefined); - results.savedObjectsCreated[job.jobId] = { success: true, type }; + results.savedObjectsCreated[type]![job.jobId] = { success: true }; } catch (error) { - results.savedObjectsCreated[job.jobId] = { + results.savedObjectsCreated[type]![job.jobId] = { success: false, - type, + error: getSavedObjectClientError(error), }; } @@ -75,22 +81,62 @@ export function syncSavedObjectsFactory( for (const job of status.jobs['data-frame-analytics']) { if (job.checks.savedObjectExits === false) { const type = 'data-frame-analytics'; + if (results.savedObjectsCreated[type] === undefined) { + results.savedObjectsCreated[type] = {}; + } if (simulate === true) { - results.savedObjectsCreated[job.jobId] = { success: true, type }; + results.savedObjectsCreated[type]![job.jobId] = { success: true }; } else { // create DFA saved objects for jobs which are missing them const jobId = job.jobId; tasks.push(async () => { try { await jobSavedObjectService.createDataFrameAnalyticsJob(jobId); - results.savedObjectsCreated[job.jobId] = { + results.savedObjectsCreated[type]![job.jobId] = { success: true, - type, }; } catch (error) { - results.savedObjectsCreated[job.jobId] = { + results.savedObjectsCreated[type]![job.jobId] = { success: false, - type, + + error: getSavedObjectClientError(error), + }; + } + }); + } + } + } + + for (const model of status.jobs['trained-model']) { + if (model.checks.savedObjectExits === false) { + const { modelId } = model; + const type = 'trained-model'; + if (results.savedObjectsCreated[type] === undefined) { + results.savedObjectsCreated[type] = {}; + } + if (simulate === true) { + results.savedObjectsCreated[type]![modelId] = { success: true }; + } else { + // create model saved objects for models which are missing them + tasks.push(async () => { + try { + const mod = models.trained_model_configs.find((m) => m.model_id === modelId); + if (mod === undefined) { + results.savedObjectsCreated[type]![modelId] = { + success: false, + error: `trained model ${modelId} not found`, + }; + return; + } + const job = getJobDetailsFromTrainedModel(mod); + await jobSavedObjectService.createTrainedModel(modelId, job); + results.savedObjectsCreated[type]![modelId] = { + success: true, + }; + } catch (error) { + results.savedObjectsCreated[type]![modelId] = { + success: false, + error: getSavedObjectClientError(error), }; } @@ -102,8 +148,11 @@ export function syncSavedObjectsFactory( for (const job of status.savedObjects['anomaly-detector']) { if (job.checks.jobExists === false) { const type = 'anomaly-detector'; + if (results.savedObjectsDeleted[type] === undefined) { + results.savedObjectsDeleted[type] = {}; + } if (simulate === true) { - results.savedObjectsDeleted[job.jobId] = { success: true, type }; + results.savedObjectsDeleted[type]![job.jobId] = { success: true }; } else { // Delete AD saved objects for jobs which no longer exist const { jobId, namespaces } = job; @@ -114,11 +163,11 @@ export function syncSavedObjectsFactory( } else { await jobSavedObjectService.deleteAnomalyDetectionJob(jobId); } - results.savedObjectsDeleted[job.jobId] = { success: true, type }; + results.savedObjectsDeleted[type]![job.jobId] = { success: true }; } catch (error) { - results.savedObjectsDeleted[job.jobId] = { + results.savedObjectsDeleted[type]![job.jobId] = { success: false, - type, + error: getSavedObjectClientError(error), }; } @@ -129,8 +178,11 @@ export function syncSavedObjectsFactory( for (const job of status.savedObjects['data-frame-analytics']) { if (job.checks.jobExists === false) { const type = 'data-frame-analytics'; + if (results.savedObjectsDeleted[type] === undefined) { + results.savedObjectsDeleted[type] = {}; + } if (simulate === true) { - results.savedObjectsDeleted[job.jobId] = { success: true, type }; + results.savedObjectsDeleted[type]![job.jobId] = { success: true }; } else { // Delete DFA saved objects for jobs which no longer exist const { jobId, namespaces } = job; @@ -141,14 +193,47 @@ export function syncSavedObjectsFactory( } else { await jobSavedObjectService.deleteDataFrameAnalyticsJob(jobId); } - results.savedObjectsDeleted[job.jobId] = { + results.savedObjectsDeleted[type]![job.jobId] = { success: true, - type, }; } catch (error) { - results.savedObjectsDeleted[job.jobId] = { + results.savedObjectsDeleted[type]![job.jobId] = { success: false, - type, + + error: getSavedObjectClientError(error), + }; + } + }); + } + } + } + + for (const model of status.savedObjects['trained-model']) { + if (model.checks.trainedModelExists === false) { + const { modelId, namespaces } = model; + const type = 'trained-model'; + if (results.savedObjectsDeleted[type] === undefined) { + results.savedObjectsDeleted[type] = {}; + } + + if (simulate === true) { + results.savedObjectsDeleted[type]![modelId] = { success: true }; + } else { + // Delete model saved objects for models which no longer exist + tasks.push(async () => { + try { + if (namespaces !== undefined && namespaces.length) { + await jobSavedObjectService.forceDeleteTrainedModel(modelId, namespaces[0]); + } else { + await jobSavedObjectService.deleteTrainedModel(modelId); + } + results.savedObjectsDeleted[type]![modelId] = { + success: true, + }; + } catch (error) { + results.savedObjectsDeleted[type]![modelId] = { + success: false, + error: getSavedObjectClientError(error), }; } @@ -166,10 +251,13 @@ export function syncSavedObjectsFactory( adJobsById[job.jobId] && adJobsById[job.jobId].datafeedId !== job.datafeedId) ) { + if (results.datafeedsAdded[type] === undefined) { + results.datafeedsAdded[type] = {}; + } // add datafeed id for jobs where the datafeed exists but the id is missing from the saved object // or if the datafeed id in the saved object is not the same as the one attached to the job in es if (simulate === true) { - results.datafeedsAdded[job.jobId] = { success: true, type }; + results.datafeedsAdded[type]![job.jobId] = { success: true }; } else { const df = datafeeds.datafeeds.find((d) => d.job_id === job.jobId); const jobId = job.jobId; @@ -180,11 +268,11 @@ export function syncSavedObjectsFactory( if (datafeedId !== undefined) { await jobSavedObjectService.addDatafeed(datafeedId, jobId); } - results.datafeedsAdded[job.jobId] = { success: true, type }; + results.datafeedsAdded[type]![job.jobId] = { success: true }; } catch (error) { - results.datafeedsAdded[job.jobId] = { + results.datafeedsAdded[type]![job.jobId] = { success: false, - type, + error: getSavedObjectClientError(error), }; } @@ -196,19 +284,22 @@ export function syncSavedObjectsFactory( job.datafeedId !== null && job.datafeedId !== undefined ) { + if (results.datafeedsRemoved[type] === undefined) { + results.datafeedsRemoved[type] = {}; + } // remove datafeed id for jobs where the datafeed no longer exists but the id is populated in the saved object if (simulate === true) { - results.datafeedsRemoved[job.jobId] = { success: true, type }; + results.datafeedsRemoved[type]![job.jobId] = { success: true }; } else { const datafeedId = job.datafeedId; tasks.push(async () => { try { await jobSavedObjectService.deleteDatafeed(datafeedId); - results.datafeedsRemoved[job.jobId] = { success: true, type }; + results.datafeedsRemoved[type]![job.jobId] = { success: true }; } catch (error) { - results.datafeedsRemoved[job.jobId] = { + results.datafeedsRemoved[type]![job.jobId] = { success: false, - type, + error: getSavedObjectClientError(error), }; } @@ -227,6 +318,7 @@ export function syncSavedObjectsFactory( const results: InitializeSavedObjectResponse = { jobs: [], datafeeds: [], + trainedModels: [], success: true, }; const status = await checkStatus(); @@ -235,7 +327,7 @@ export function syncSavedObjectsFactory( return acc; }, {} as Record); - const jobs: Array<{ job: JobObject; namespaces: string[] }> = []; + const jobObjects: Array<{ job: JobObject; namespaces: string[] }> = []; const datafeeds: Array<{ jobId: string; datafeedId: string }> = []; const types: JobType[] = ['anomaly-detector', 'data-frame-analytics']; @@ -245,14 +337,14 @@ export function syncSavedObjectsFactory( if (simulate === true) { results.jobs.push({ id: job.jobId, type }); } else { - jobs.push({ + jobObjects.push({ job: { job_id: job.jobId, datafeed_id: job.datafeedId ?? null, type, }, // allow some jobs to be assigned to specific spaces when initializing - namespaces: spaceOverrides?.overrides[type][job.jobId] ?? ['*'], + namespaces: spaceOverrides?.overrides[type]![job.jobId] ?? ['*'], }); } } @@ -284,10 +376,40 @@ export function syncSavedObjectsFactory( } }); + const models = status.jobs['trained-model'].filter( + ({ checks }) => checks.savedObjectExits === false + ); + const modelObjects: TrainedModelObject[] = []; + + if (models.length) { + if (simulate === true) { + results.trainedModels = models.map(({ modelId }) => ({ id: modelId })); + } else { + const { trained_model_configs: trainedModelConfigs } = + await client.asInternalUser.ml.getTrainedModels({ + model_id: models.map(({ modelId }) => modelId).join(','), + }); + const jobDetails = trainedModelConfigs.reduce((acc, cur) => { + const job = getJobDetailsFromTrainedModel(cur); + if (job !== null) { + acc[cur.model_id] = job; + } + return acc; + }, {} as Record); + + models.forEach(({ modelId }) => { + modelObjects.push({ + model_id: modelId, + job: jobDetails[modelId] ? jobDetails[modelId] : null, + }); + }); + } + } + try { // create missing job saved objects - const createResults = await jobSavedObjectService.bulkCreateJobs(jobs); - createResults.saved_objects.forEach(({ attributes }) => { + const createJobsResults = await jobSavedObjectService.bulkCreateJobs(jobObjects); + createJobsResults.saved_objects.forEach(({ attributes }) => { results.jobs.push({ id: attributes.job_id, type: attributes.type, @@ -302,6 +424,17 @@ export function syncSavedObjectsFactory( type: 'anomaly-detector', }); } + + // use * space if no spaces for related jobs can be found. + const createModelsResults = await jobSavedObjectService.bulkCreateTrainedModel( + modelObjects, + '*' + ); + createModelsResults.saved_objects.forEach(({ attributes }) => { + results.trainedModels.push({ + id: attributes.model_id, + }); + }); } catch (error) { results.success = false; results.error = Boom.boomify(error); @@ -310,14 +443,17 @@ export function syncSavedObjectsFactory( return results; } - async function isSyncNeeded(jobType: JobType) { - const { jobs, datafeeds } = await initSavedObjects(true); + async function isSyncNeeded(jobType?: JobType | TrainedModelType) { + const { jobs, datafeeds, trainedModels } = await initSavedObjects(true); const missingJobs = jobs.length > 0 && (jobType === undefined || jobs.some(({ type }) => type === jobType)); + const missingModels = + trainedModels.length > 0 && (jobType === undefined || jobType === 'trained-model'); + const missingDatafeeds = datafeeds.length > 0 && jobType !== 'data-frame-analytics'; - return missingJobs || missingDatafeeds; + return missingJobs || missingModels || missingDatafeeds; } return { checkStatus, syncSavedObjects, initSavedObjects, isSyncNeeded }; diff --git a/x-pack/plugins/ml/server/saved_objects/sync_task.ts b/x-pack/plugins/ml/server/saved_objects/sync_task.ts new file mode 100644 index 0000000000000..eeb86cd11d0d5 --- /dev/null +++ b/x-pack/plugins/ml/server/saved_objects/sync_task.ts @@ -0,0 +1,141 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger, CoreStart, IScopedClusterClient } from 'kibana/server'; +import { + ConcreteTaskInstance, + TaskManagerSetupContract, + TaskManagerStartContract, + TaskInstance, +} from '../../../task_manager/server'; +import type { SecurityPluginSetup } from '../../../security/server'; +import { savedObjectClientsFactory } from './util'; +import { jobSavedObjectServiceFactory } from './service'; +import { syncSavedObjectsFactory } from './sync'; + +const SAVED_OBJECTS_SYNC_TASK_TYPE = 'ML:saved-objects-sync'; +const SAVED_OBJECTS_SYNC_TASK_ID = 'ML:saved-objects-sync-task'; +const SAVED_OBJECTS_SYNC_INTERVAL_DEFAULT = '1h'; + +export class SavedObjectsSyncService { + private core: CoreStart | null = null; + private log: { error: (t: string, e?: Error) => void; [l: string]: (t: string) => void }; + + constructor(logger: Logger) { + this.log = createLocalLogger(logger, `Task ${SAVED_OBJECTS_SYNC_TASK_ID}: `); + } + + public registerSyncTask( + taskManager: TaskManagerSetupContract, + security: SecurityPluginSetup | undefined, + spacesEnabled: boolean, + isMlReady: () => Promise + ) { + taskManager.registerTaskDefinitions({ + [SAVED_OBJECTS_SYNC_TASK_TYPE]: { + title: 'ML saved objet sync', + description: "This task periodically syncs ML's saved objects", + timeout: '1m', + maxAttempts: 3, + maxConcurrency: 1, + + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + return { + run: async () => { + await isMlReady(); + const core = this.core; + const { state } = taskInstance; + + if (core === null || security === null || spacesEnabled === null) { + const error = 'dependencies not initialized'; + this.log.error(error); + throw new Error(error); + } + const client = core.elasticsearch.client as unknown as IScopedClusterClient; + + const { getInternalSavedObjectsClient } = savedObjectClientsFactory( + () => core.savedObjects + ); + const savedObjectsClient = getInternalSavedObjectsClient(); + if (savedObjectsClient === null) { + const error = 'Internal saved object client not initialized'; + this.log.error(error); + throw new Error(error); + } + + const jobSavedObjectService = jobSavedObjectServiceFactory( + savedObjectsClient, + savedObjectsClient, + spacesEnabled, + security?.authz, + client, + isMlReady + ); + const { initSavedObjects } = syncSavedObjectsFactory(client, jobSavedObjectService); + const { jobs, trainedModels } = await initSavedObjects(false); + const count = jobs.length + trainedModels.length; + + this.log.info( + count + ? `${count} ML saved object${count > 1 ? 's' : ''} synced` + : 'No ML saved objects in need of synchronization' + ); + + return { + state: { + runs: (state.runs ?? 0) + 1, + totalSavedObjectsSynced: (state.totalSavedObjectsSynced ?? 0) + count, + }, + }; + }, + cancel: async () => { + this.log.warn('timed out'); + }, + }; + }, + }, + }); + } + + public async scheduleSyncTask( + taskManager: TaskManagerStartContract, + core: CoreStart + ): Promise { + this.core = core; + try { + await taskManager.removeIfExists(SAVED_OBJECTS_SYNC_TASK_ID); + const taskInstance = await taskManager.ensureScheduled({ + id: SAVED_OBJECTS_SYNC_TASK_ID, + taskType: SAVED_OBJECTS_SYNC_TASK_TYPE, + schedule: { + interval: SAVED_OBJECTS_SYNC_INTERVAL_DEFAULT, + }, + params: {}, + state: { + runs: 0, + totalSavedObjectsSynced: 0, + }, + scope: ['ml'], + }); + + this.log.info(`scheduled with interval ${taskInstance.schedule?.interval}`); + + return taskInstance; + } catch (e) { + this.log.error(`Error running task: ${SAVED_OBJECTS_SYNC_TASK_ID}, `, e?.message() ?? e); + return null; + } + } +} + +function createLocalLogger(logger: Logger, preText: string) { + return { + info: (text: string) => logger.info(`${preText}${text}`), + warn: (text: string) => logger.warn(`${preText}${text}`), + error: (text: string, e?: Error) => logger.error(`${preText}${text} ${e ?? ''}`), + }; +} diff --git a/x-pack/plugins/ml/server/saved_objects/util.ts b/x-pack/plugins/ml/server/saved_objects/util.ts index 76c8b332ee525..f16f80a20390c 100644 --- a/x-pack/plugins/ml/server/saved_objects/util.ts +++ b/x-pack/plugins/ml/server/saved_objects/util.ts @@ -5,9 +5,11 @@ * 2.0. */ +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { SavedObjectsServiceStart, KibanaRequest } from 'kibana/server'; import { SavedObjectsClient } from '../../../../../src/core/server'; -import { ML_SAVED_OBJECT_TYPE } from '../../common/types/saved_objects'; +import { ML_JOB_SAVED_OBJECT_TYPE } from '../../common/types/saved_objects'; +import type { TrainedModelJob } from './service'; export function savedObjectClientsFactory( getSavedObjectsStart: () => SavedObjectsServiceStart | null @@ -21,7 +23,7 @@ export function savedObjectClientsFactory( return null; } return savedObjectsStart.getScopedClient(request, { - includedHiddenTypes: [ML_SAVED_OBJECT_TYPE], + includedHiddenTypes: [ML_JOB_SAVED_OBJECT_TYPE], }); }, // create a saved object client which has access to all saved objects @@ -40,3 +42,18 @@ export function savedObjectClientsFactory( export function getSavedObjectClientError(error: any) { return error.isBoom && error.output?.payload ? error.output.payload : error.body ?? error; } + +export function getJobDetailsFromTrainedModel( + model: estypes.MlTrainedModelConfig | estypes.MlPutTrainedModelRequest['body'] +): TrainedModelJob | null { + // @ts-ignore types are wrong + if (model.metadata?.analytics_config === undefined) { + return null; + } + + // @ts-ignore types are wrong + const jobId: string = model.metadata.analytics_config.id; + // @ts-ignore types are wrong + const createTime: number = model.metadata.analytics_config.create_time; + return { job_id: jobId, create_time: createTime }; +} diff --git a/x-pack/plugins/ml/server/shared_services/shared_services.ts b/x-pack/plugins/ml/server/shared_services/shared_services.ts index 5cce757fc7573..16dd3cf7bec97 100644 --- a/x-pack/plugins/ml/server/shared_services/shared_services.ts +++ b/x-pack/plugins/ml/server/shared_services/shared_services.ts @@ -201,13 +201,16 @@ function getRequestItemsProvider( // will not receive a real request object when being called from an alert. // instead a dummy request object will be supplied const clusterClient = getClusterClient(); - const jobSavedObjectService = jobSavedObjectServiceFactory( - savedObjectsClient, - internalSavedObjectsClient, - spaceEnabled, - authorization, - isMlReady - ); + const getSobSavedObjectService = (client: IScopedClusterClient) => { + return jobSavedObjectServiceFactory( + savedObjectsClient, + internalSavedObjectsClient, + spaceEnabled, + authorization, + client, + isMlReady + ); + }; if (clusterClient === null) { throw new MLClusterClientUninitialized(`ML's cluster client has not been initialized`); @@ -235,9 +238,11 @@ function getRequestItemsProvider( return fieldFormatRegistry; }; + let jobSavedObjectService; if (request instanceof KibanaRequest) { hasMlCapabilities = getHasMlCapabilities(request); scopedClient = clusterClient.asScoped(request); + jobSavedObjectService = getSobSavedObjectService(scopedClient); mlClient = getMlClient(scopedClient, jobSavedObjectService); } else { hasMlCapabilities = () => Promise.resolve(); @@ -246,6 +251,7 @@ function getRequestItemsProvider( asInternalUser, asCurrentUser: asInternalUser, }; + jobSavedObjectService = getSobSavedObjectService(scopedClient); mlClient = getMlClient(scopedClient, jobSavedObjectService); } diff --git a/x-pack/plugins/ml/server/types.ts b/x-pack/plugins/ml/server/types.ts index d5c67bf99a7a0..8120d5c880b6c 100644 --- a/x-pack/plugins/ml/server/types.ts +++ b/x-pack/plugins/ml/server/types.ts @@ -28,6 +28,7 @@ import type { FieldFormatsSetup, FieldFormatsStart, } from '../../../../src/plugins/field_formats/server'; +import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; export interface LicenseCheckResult { isAvailable: boolean; @@ -61,6 +62,7 @@ export interface PluginsSetup { alerting?: AlertingPlugin['setup']; actions?: ActionsPlugin['setup']; usageCollection?: UsageCollectionSetup; + taskManager: TaskManagerSetupContract; } export interface PluginsStart { @@ -68,6 +70,7 @@ export interface PluginsStart { dataViews: DataViewsPluginStart; fieldFormats: FieldFormatsStart; spaces?: SpacesPluginStart; + taskManager: TaskManagerStartContract; } export interface RouteInitialization { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b314e0c88e686..59dd27f94d1e5 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -18063,7 +18063,6 @@ "xpack.ml.management.jobsList.noPermissionToAccessLabel": "アクセスが拒否されました", "xpack.ml.management.jobsList.syncFlyoutButton": "保存されたオブジェクトを同期", "xpack.ml.management.jobsListTitle": "機械学習ジョブ", - "xpack.ml.management.jobsSpacesList.objectNoun": "ジョブ", "xpack.ml.management.jobsSpacesList.updateSpaces.error": "{id} の更新エラー", "xpack.ml.management.syncSavedObjectsFlyout.closeButton": "閉じる", "xpack.ml.management.syncSavedObjectsFlyout.datafeedsAdded.description": "異常検知のデータフィード ID がない保存されたオブジェクトがある場合は、ID が追加されます。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3797f746d4103..5af6782572cda 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -18090,7 +18090,6 @@ "xpack.ml.management.jobsList.noPermissionToAccessLabel": "访问被拒绝", "xpack.ml.management.jobsList.syncFlyoutButton": "同步已保存对象", "xpack.ml.management.jobsListTitle": "Machine Learning 作业", - "xpack.ml.management.jobsSpacesList.objectNoun": "作业", "xpack.ml.management.jobsSpacesList.updateSpaces.error": "更新 {id} 时出错", "xpack.ml.management.syncSavedObjectsFlyout.closeButton": "关闭", "xpack.ml.management.syncSavedObjectsFlyout.datafeedsAdded.description": "如果有已保存对象缺失异常检测作业的数据馈送 ID,则将添加该 ID。", diff --git a/x-pack/test/api_integration/apis/ml/saved_objects/initialize.ts b/x-pack/test/api_integration/apis/ml/saved_objects/initialize.ts index 1684a9c5465bf..47aa9603a645e 100644 --- a/x-pack/test/api_integration/apis/ml/saved_objects/initialize.ts +++ b/x-pack/test/api_integration/apis/ml/saved_objects/initialize.ts @@ -68,7 +68,7 @@ export default ({ getService }: FtrProviderContext) => { it('should not initialize jobs if all jobs have spaces assigned', async () => { const body = await runRequest(USER.ML_POWERUSER_ALL_SPACES, 200); - expect(body).to.eql({ datafeeds: [], jobs: [], success: true }); + expect(body).to.eql({ datafeeds: [], jobs: [], trainedModels: [], success: true }); await ml.api.assertJobSpaces(adJobId, 'anomaly-detector', ['*']); await ml.api.assertJobSpaces(dfaJobId, 'data-frame-analytics', ['*']); }); diff --git a/x-pack/test/api_integration/apis/ml/saved_objects/sync.ts b/x-pack/test/api_integration/apis/ml/saved_objects/sync.ts index 4038c03f4d953..b5d37730eb787 100644 --- a/x-pack/test/api_integration/apis/ml/saved_objects/sync.ts +++ b/x-pack/test/api_integration/apis/ml/saved_objects/sync.ts @@ -109,10 +109,18 @@ export default ({ getService }: FtrProviderContext) => { const body = await runSyncRequest(USER.ML_POWERUSER_ALL_SPACES, 200); expect(body).to.eql({ - datafeedsAdded: { [adJobId2]: { success: true, type: 'anomaly-detector' } }, - datafeedsRemoved: { [adJobId3]: { success: true, type: 'anomaly-detector' } }, - savedObjectsCreated: { [adJobIdES]: { success: true, type: 'anomaly-detector' } }, - savedObjectsDeleted: { [adJobId1]: { success: true, type: 'anomaly-detector' } }, + datafeedsAdded: { + 'anomaly-detector': { [adJobId2]: { success: true } }, + }, + datafeedsRemoved: { + 'anomaly-detector': { [adJobId3]: { success: true } }, + }, + savedObjectsCreated: { + 'anomaly-detector': { [adJobIdES]: { success: true } }, + }, + savedObjectsDeleted: { + 'anomaly-detector': { [adJobId1]: { success: true } }, + }, }); }); @@ -146,7 +154,9 @@ export default ({ getService }: FtrProviderContext) => { // expect datafeed to be added expect(body).to.eql({ - datafeedsAdded: { [adJobId1]: { success: true, type: 'anomaly-detector' } }, + datafeedsAdded: { + 'anomaly-detector': { [adJobId1]: { success: true } }, + }, datafeedsRemoved: {}, savedObjectsCreated: {}, savedObjectsDeleted: {}, @@ -172,7 +182,9 @@ export default ({ getService }: FtrProviderContext) => { // previous datafeed should be removed and new datafeed should be added on sync expect(body2).to.eql({ - datafeedsAdded: { [adJobId1]: { success: true, type: 'anomaly-detector' } }, + datafeedsAdded: { + 'anomaly-detector': { [adJobId1]: { success: true } }, + }, datafeedsRemoved: {}, savedObjectsCreated: {}, savedObjectsDeleted: {}, diff --git a/x-pack/test/api_integration/apis/ml/saved_objects/update_jobs_spaces.ts b/x-pack/test/api_integration/apis/ml/saved_objects/update_jobs_spaces.ts index f326a3049e921..2a8fc9b3d230a 100644 --- a/x-pack/test/api_integration/apis/ml/saved_objects/update_jobs_spaces.ts +++ b/x-pack/test/api_integration/apis/ml/saved_objects/update_jobs_spaces.ts @@ -82,7 +82,7 @@ export default ({ getService }: FtrProviderContext) => { USER.ML_POWERUSER_SPACE1 ); - expect(body).to.eql({ [adJobId]: { success: true } }); + expect(body).to.eql({ [adJobId]: { type: 'ml-job', success: true } }); await ml.api.assertJobSpaces(adJobId, 'anomaly-detector', [idSpace1]); }); @@ -99,7 +99,7 @@ export default ({ getService }: FtrProviderContext) => { USER.ML_POWERUSER_SPACE1 ); - expect(body).to.eql({ [dfaJobId]: { success: true } }); + expect(body).to.eql({ [dfaJobId]: { type: 'ml-job', success: true } }); await ml.api.assertJobSpaces(dfaJobId, 'data-frame-analytics', [idSpace1]); }); diff --git a/x-pack/test/api_integration/apis/ml/system/capabilities.ts b/x-pack/test/api_integration/apis/ml/system/capabilities.ts index d0df53dfee343..aba433cc92e4a 100644 --- a/x-pack/test/api_integration/apis/ml/system/capabilities.ts +++ b/x-pack/test/api_integration/apis/ml/system/capabilities.ts @@ -12,6 +12,8 @@ import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/commo import { USER } from '../../../../functional/services/ml/security_common'; import { MlCapabilitiesResponse } from '../../../../../plugins/ml/common/types/capabilities'; +const NUMBER_OF_CAPABILITIES = 36; + export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertestWithoutAuth'); const ml = getService('ml'); @@ -45,7 +47,7 @@ export default ({ getService }: FtrProviderContext) => { it('should have the right number of capabilities', async () => { const { capabilities } = await runRequest(USER.ML_POWERUSER); - expect(Object.keys(capabilities).length).to.eql(32); + expect(Object.keys(capabilities).length).to.eql(NUMBER_OF_CAPABILITIES); }); it('should get viewer capabilities', async () => { @@ -84,6 +86,10 @@ export default ({ getService }: FtrProviderContext) => { canCreateAnnotation: true, canDeleteAnnotation: true, canViewMlNodes: false, + canGetTrainedModels: true, + canCreateTrainedModels: false, + canDeleteTrainedModels: false, + canStartStopTrainedModels: false, }); }); @@ -123,6 +129,10 @@ export default ({ getService }: FtrProviderContext) => { canCreateAnnotation: true, canDeleteAnnotation: true, canViewMlNodes: true, + canGetTrainedModels: true, + canCreateTrainedModels: true, + canDeleteTrainedModels: true, + canStartStopTrainedModels: true, }); }); }); diff --git a/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts b/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts index b51b87457caa2..cbd38cdfdc614 100644 --- a/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts +++ b/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts @@ -15,6 +15,8 @@ import { MlCapabilitiesResponse } from '../../../../../plugins/ml/common/types/c const idSpaceWithMl = 'space_with_ml'; const idSpaceNoMl = 'space_no_ml'; +const NUMBER_OF_CAPABILITIES = 36; + export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertestWithoutAuth'); const spacesService = getService('spaces'); @@ -71,11 +73,11 @@ export default ({ getService }: FtrProviderContext) => { it('should have the right number of capabilities - space with ML', async () => { const { capabilities } = await runRequest(USER.ML_POWERUSER, idSpaceWithMl); - expect(Object.keys(capabilities).length).to.eql(32); + expect(Object.keys(capabilities).length).to.eql(NUMBER_OF_CAPABILITIES); }); it('should have the right number of capabilities - space without ML', async () => { const { capabilities } = await runRequest(USER.ML_POWERUSER, idSpaceNoMl); - expect(Object.keys(capabilities).length).to.eql(32); + expect(Object.keys(capabilities).length).to.eql(NUMBER_OF_CAPABILITIES); }); it('should get viewer capabilities - space with ML', async () => { @@ -113,6 +115,10 @@ export default ({ getService }: FtrProviderContext) => { canCreateAnnotation: true, canDeleteAnnotation: true, canViewMlNodes: false, + canGetTrainedModels: true, + canCreateTrainedModels: false, + canDeleteTrainedModels: false, + canStartStopTrainedModels: false, }); }); @@ -151,6 +157,10 @@ export default ({ getService }: FtrProviderContext) => { canCreateAnnotation: false, canDeleteAnnotation: false, canViewMlNodes: false, + canGetTrainedModels: false, + canCreateTrainedModels: false, + canDeleteTrainedModels: false, + canStartStopTrainedModels: false, }); }); @@ -189,6 +199,10 @@ export default ({ getService }: FtrProviderContext) => { canCreateAnnotation: true, canDeleteAnnotation: true, canViewMlNodes: true, + canGetTrainedModels: true, + canCreateTrainedModels: true, + canDeleteTrainedModels: true, + canStartStopTrainedModels: true, }); }); @@ -227,6 +241,10 @@ export default ({ getService }: FtrProviderContext) => { canCreateAnnotation: false, canDeleteAnnotation: false, canViewMlNodes: false, + canGetTrainedModels: false, + canCreateTrainedModels: false, + canDeleteTrainedModels: false, + canStartStopTrainedModels: false, }); }); }); diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 8d9ba26961940..a93d85cc91855 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -986,6 +986,7 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { await this.runDFAJob(dfaConfig.id); await this.waitForDFAJobTrainingRecordCountToBePositive(dfaConfig.id); await this.waitForAnalyticsState(dfaConfig.id, DATA_FRAME_TASK_STATE.STOPPED, timeout); + await this.syncSavedObjects(); }, async updateJobSpaces( @@ -1001,7 +1002,7 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { .send({ jobType, jobIds: [jobId], spacesToAdd, spacesToRemove }) .expect(200); - expect(body).to.eql({ [jobId]: { success: true } }); + expect(body).to.eql({ [jobId]: { success: true, type: 'ml-job' } }); }, async assertJobSpaces(jobId: string, jobType: JobType, expectedSpaces: string[]) { @@ -1024,10 +1025,19 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { } }, - async createTrainedModel(modelId: string, body: PutTrainedModelConfig) { + async syncSavedObjects(simulate: boolean = false, space?: string) { + const { body } = await kbnSupertest + .get(`${space ? `/s/${space}` : ''}/api/ml/saved_objects/sync?simulate=${simulate}`) + .set(COMMON_REQUEST_HEADERS) + .expect(200); + return body; + }, + + async createTrainedModel(modelId: string, body: PutTrainedModelConfig, space?: string) { log.debug(`Creating trained model with id "${modelId}"`); - const model = await esSupertest - .put(`/_ml/trained_models/${modelId}`) + const model = await kbnSupertest + .put(`${space ? `/s/${space}` : ''}/api/ml/trained_models/${modelId}`) + .set(COMMON_REQUEST_HEADERS) .send(body) .expect(200) .then((res: any) => res.body); @@ -1089,7 +1099,7 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { async createModelAlias(modelId: string, modelAlias: string) { log.debug(`Creating alias for model "${modelId}"`); await esSupertest - .put(`/_ml/trained_models/${modelId}/model_aliases/${modelAlias}`) + .put(`/_ml/trained_models/${modelId}/model_aliases/${modelAlias}?reassign=true`) .expect(200); log.debug('> Model alias created'); }, diff --git a/x-pack/test/functional/services/ml/stack_management_jobs.ts b/x-pack/test/functional/services/ml/stack_management_jobs.ts index a7b6fa2f94617..63833c1339564 100644 --- a/x-pack/test/functional/services/ml/stack_management_jobs.ts +++ b/x-pack/test/functional/services/ml/stack_management_jobs.ts @@ -92,7 +92,7 @@ export function MachineLearningStackManagementJobsProvider( const resultToast = await toasts.getToastElement(1); const titleElement = await testSubjects.findDescendant('euiToastHeader', resultToast); const title: string = await titleElement.getVisibleText(); - expect(title).to.match(/^\d+ job[s]? synchronized$/); + expect(title).to.match(/^\d+ item[s]? synchronized$/); const dismissButton = await testSubjects.findDescendant('toastCloseButton', resultToast); await dismissButton.click(); From 67e52e75fe204d9872fd666bcb9d7d02aa832712 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Wed, 9 Mar 2022 13:36:06 -0500 Subject: [PATCH 133/140] [ResponseOps] Exclude RBAC from SIEM for alerts search strategy (#126859) * Exclude RBAC from siem requests * PR feedback * PR feedback * Fix tests --- .../search_strategy/search_strategy.test.ts | 112 +++++++++++++++++- .../server/search_strategy/search_strategy.ts | 38 ++++-- x-pack/test/common/services/bsearch_secure.ts | 94 +++++++++++++++ x-pack/test/common/services/index.ts | 2 + .../common/lib/authentication/roles.ts | 49 ++++++++ .../common/lib/authentication/users.ts | 16 +++ .../tests/basic/search_strategy.ts | 99 +++++++++++----- 7 files changed, 368 insertions(+), 42 deletions(-) create mode 100644 x-pack/test/common/services/bsearch_secure.ts diff --git a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts index 9f83930dadc69..9d74abc17eb3d 100644 --- a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts +++ b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts @@ -68,6 +68,7 @@ describe('ruleRegistrySearchStrategyProvider()', () => { }); let getAuthzFilterSpy: jest.SpyInstance; + const searchStrategySearch = jest.fn().mockImplementation(() => of(response)); beforeEach(() => { ruleDataService.findIndicesByFeature.mockImplementation(() => { @@ -80,10 +81,14 @@ describe('ruleRegistrySearchStrategyProvider()', () => { data.search.getSearchStrategy.mockImplementation(() => { return { - search: () => of(response), + search: searchStrategySearch, }; }); + (data.search.searchAsInternalUser.search as jest.Mock).mockImplementation(() => { + return of(response); + }); + getAuthzFilterSpy = jest .spyOn(getAuthzFilterImport, 'getAuthzFilter') .mockImplementation(async () => { @@ -94,7 +99,9 @@ describe('ruleRegistrySearchStrategyProvider()', () => { afterEach(() => { ruleDataService.findIndicesByFeature.mockClear(); data.search.getSearchStrategy.mockClear(); + (data.search.searchAsInternalUser.search as jest.Mock).mockClear(); getAuthzFilterSpy.mockClear(); + searchStrategySearch.mockClear(); }); it('should handle a basic search request', async () => { @@ -199,4 +206,107 @@ describe('ruleRegistrySearchStrategyProvider()', () => { .toPromise(); expect(result).toBe(EMPTY_RESPONSE); }); + + it('should not apply rbac filters for siem', async () => { + const request: RuleRegistrySearchRequest = { + featureIds: [AlertConsumers.SIEM], + }; + const options = {}; + const deps = { + request: {}, + }; + + const strategy = ruleRegistrySearchStrategyProvider( + data, + ruleDataService, + alerting, + logger, + security, + spaces + ); + + await strategy + .search(request, options, deps as unknown as SearchStrategyDependencies) + .toPromise(); + expect(getAuthzFilterSpy).not.toHaveBeenCalled(); + }); + + it('should throw an error if requesting multiple featureIds and one is SIEM', async () => { + const request: RuleRegistrySearchRequest = { + featureIds: [AlertConsumers.SIEM, AlertConsumers.LOGS], + }; + const options = {}; + const deps = { + request: {}, + }; + + const strategy = ruleRegistrySearchStrategyProvider( + data, + ruleDataService, + alerting, + logger, + security, + spaces + ); + + let err; + try { + await strategy + .search(request, options, deps as unknown as SearchStrategyDependencies) + .toPromise(); + } catch (e) { + err = e; + } + expect(err).toBeDefined(); + }); + + it('should use internal user when requesting o11y alerts as RBAC is applied', async () => { + const request: RuleRegistrySearchRequest = { + featureIds: [AlertConsumers.LOGS], + }; + const options = {}; + const deps = { + request: {}, + }; + + const strategy = ruleRegistrySearchStrategyProvider( + data, + ruleDataService, + alerting, + logger, + security, + spaces + ); + + await strategy + .search(request, options, deps as unknown as SearchStrategyDependencies) + .toPromise(); + expect(data.search.searchAsInternalUser.search).toHaveBeenCalled(); + expect(searchStrategySearch).not.toHaveBeenCalled(); + }); + + it('should use scoped user when requesting siem alerts as RBAC is not applied', async () => { + const request: RuleRegistrySearchRequest = { + featureIds: [AlertConsumers.SIEM], + }; + const options = {}; + const deps = { + request: {}, + }; + + const strategy = ruleRegistrySearchStrategyProvider( + data, + ruleDataService, + alerting, + logger, + security, + spaces + ); + + await strategy + .search(request, options, deps as unknown as SearchStrategyDependencies) + .toPromise(); + expect(data.search.searchAsInternalUser.search).not.toHaveBeenCalled(); + expect(searchStrategySearch).toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts index dd7f392b0a268..78d656c76ef12 100644 --- a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts +++ b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts @@ -8,7 +8,7 @@ import { map, mergeMap, catchError } from 'rxjs/operators'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { Logger } from 'src/core/server'; import { from, of } from 'rxjs'; -import { isValidFeatureId } from '@kbn/rule-data-utils'; +import { isValidFeatureId, AlertConsumers } from '@kbn/rule-data-utils'; import { ENHANCED_ES_SEARCH_STRATEGY } from '../../../../../src/plugins/data/common'; import { ISearchStrategy, PluginStart } from '../../../../../src/plugins/data/server'; import { @@ -36,10 +36,22 @@ export const ruleRegistrySearchStrategyProvider = ( security?: SecurityPluginSetup, spaces?: SpacesPluginStart ): ISearchStrategy => { - const es = data.search.getSearchStrategy(ENHANCED_ES_SEARCH_STRATEGY); - + const internalUserEs = data.search.searchAsInternalUser; + const requestUserEs = data.search.getSearchStrategy(ENHANCED_ES_SEARCH_STRATEGY); return { search: (request, options, deps) => { + // SIEM uses RBAC fields in their alerts but also utilizes ES DLS which + // is different than every other solution so we need to special case + // those requests. + let siemRequest = false; + if (request.featureIds.length === 1 && request.featureIds[0] === AlertConsumers.SIEM) { + siemRequest = true; + } else if (request.featureIds.includes(AlertConsumers.SIEM)) { + throw new Error( + 'The ruleRegistryAlertsSearchStrategy search strategy is unable to accommodate requests containing multiple feature IDs and one of those IDs is SIEM.' + ); + } + const securityAuditLogger = security?.audit.asScoped(deps.request); const getActiveSpace = async () => spaces?.spacesService.getActiveSpace(deps.request); const getAsync = async () => { @@ -47,10 +59,14 @@ export const ruleRegistrySearchStrategyProvider = ( getActiveSpace(), alerting.getAlertingAuthorizationWithRequest(deps.request), ]); - const authzFilter = (await getAuthzFilter( - authorization, - ReadOperations.Find - )) as estypes.QueryDslQueryContainer; + let authzFilter; + + if (!siemRequest) { + authzFilter = (await getAuthzFilter( + authorization, + ReadOperations.Find + )) as estypes.QueryDslQueryContainer; + } return { space, authzFilter }; }; return from(getAsync()).pipe( @@ -93,7 +109,6 @@ export const ruleRegistrySearchStrategyProvider = ( const query = { bool: { - ...request.query?.bool, filter, }, }; @@ -106,7 +121,7 @@ export const ruleRegistrySearchStrategyProvider = ( query, }, }; - return es.search({ ...request, params }, options, deps); + return (siemRequest ? requestUserEs : internalUserEs).search({ params }, options, deps); }), map((response) => { // Do we have to loop over each hit? Yes. @@ -141,9 +156,8 @@ export const ruleRegistrySearchStrategyProvider = ( ); }, cancel: async (id, options, deps) => { - if (es.cancel) { - return es.cancel(id, options, deps); - } + if (internalUserEs.cancel) internalUserEs.cancel(id, options, deps); + if (requestUserEs.cancel) requestUserEs.cancel(id, options, deps); }, }; }; diff --git a/x-pack/test/common/services/bsearch_secure.ts b/x-pack/test/common/services/bsearch_secure.ts new file mode 100644 index 0000000000000..622cca92aead5 --- /dev/null +++ b/x-pack/test/common/services/bsearch_secure.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// NOTE: This is pretty much a copy/paste from test/common/services/bsearch.ts but with the ability +// to provide custom auth + +import expect from '@kbn/expect'; +import request from 'superagent'; +import type SuperTest from 'supertest'; +import { IEsSearchResponse } from 'src/plugins/data/common'; +import { FtrProviderContext } from '../ftr_provider_context'; +import { RetryService } from '../../../../test/common/services/retry/retry'; + +const parseBfetchResponse = (resp: request.Response): Array> => { + return resp.text + .trim() + .split('\n') + .map((item) => JSON.parse(item)); +}; + +const getSpaceUrlPrefix = (spaceId?: string): string => { + return spaceId && spaceId !== 'default' ? `/s/${spaceId}` : ``; +}; + +interface SendOptions { + supertestWithoutAuth: SuperTest.SuperTest; + auth: { username: string; password: string }; + options: object; + strategy: string; + space?: string; +} + +export const BSecureSearchFactory = (retry: RetryService) => ({ + send: async ({ + supertestWithoutAuth, + auth, + options, + strategy, + space, + }: SendOptions): Promise => { + const spaceUrl = getSpaceUrlPrefix(space); + const { body } = await retry.try(async () => { + const result = await supertestWithoutAuth + .post(`${spaceUrl}/internal/search/${strategy}`) + .auth(auth.username, auth.password) + .set('kbn-xsrf', 'true') + .send(options); + if (result.status === 500 || result.status === 200) { + return result; + } + throw new Error('try again'); + }); + + if (body.isRunning) { + const result = await retry.try(async () => { + const resp = await supertestWithoutAuth + .post(`${spaceUrl}/internal/bsearch`) + .auth(auth.username, auth.password) + .set('kbn-xsrf', 'true') + .send({ + batch: [ + { + request: { + id: body.id, + ...options, + }, + options: { + strategy, + }, + }, + ], + }) + .expect(200); + const [parsedResponse] = parseBfetchResponse(resp); + expect(parsedResponse.result.isRunning).equal(false); + return parsedResponse.result; + }); + return result; + } else { + return body; + } + }, +}); + +export function BSecureSearchProvider({ + getService, +}: FtrProviderContext): ReturnType { + const retry = getService('retry'); + return BSecureSearchFactory(retry); +} diff --git a/x-pack/test/common/services/index.ts b/x-pack/test/common/services/index.ts index 1eea136213043..b015e10309efb 100644 --- a/x-pack/test/common/services/index.ts +++ b/x-pack/test/common/services/index.ts @@ -9,10 +9,12 @@ import { services as kibanaCommonServices } from '../../../../test/common/servic import { services as kibanaApiIntegrationServices } from '../../../../test/api_integration/services'; import { SpacesServiceProvider } from './spaces'; +import { BSecureSearchProvider } from './bsearch_secure'; export const services = { ...kibanaCommonServices, supertest: kibanaApiIntegrationServices.supertest, spaces: SpacesServiceProvider, + secureBsearch: BSecureSearchProvider, }; diff --git a/x-pack/test/rule_registry/common/lib/authentication/roles.ts b/x-pack/test/rule_registry/common/lib/authentication/roles.ts index 098bb649ccb1a..5a75f5bc4f8da 100644 --- a/x-pack/test/rule_registry/common/lib/authentication/roles.ts +++ b/x-pack/test/rule_registry/common/lib/authentication/roles.ts @@ -221,6 +221,52 @@ export const observabilityOnlyAllSpacesAll: Role = { }, }; +export const logsOnlyAllSpacesAll: Role = { + name: 'logs_only_all_spaces_all', + privileges: { + elasticsearch: { + indices: [], + }, + kibana: [ + { + feature: { + logs: ['all'], + }, + spaces: ['*'], + }, + ], + }, +}; + +/** + * This role exists to test that the alert search strategy allows + * users who do not have access to security solutions the ability + * to see security solutions alerts. This is because security solutions + * does not properly leverage RBAC and we filter out the RBAC when + * searching for security solution alerts in the alert search strategy. + */ +export const observabilityOnlyAllSpacesAllWithReadESIndices: Role = { + name: 'obs_only_all_spaces_all_with_read_es_indices', + privileges: { + elasticsearch: { + indices: [ + { + names: ['*'], + privileges: ['all'], + }, + ], + }, + kibana: [ + { + feature: { + apm: ['all'], + }, + spaces: ['default', 'space1'], + }, + ], + }, +}; + export const observabilityOnlyReadSpacesAll: Role = { name: 'obs_only_read_all_spaces_all', privileges: { @@ -246,6 +292,7 @@ export const roles = [ observabilityOnlyAll, observabilityOnlyRead, observabilityOnlyReadSpacesAll, + observabilityOnlyAllSpacesAllWithReadESIndices, ]; /** @@ -416,7 +463,9 @@ export const allRoles = [ securitySolutionOnlyAllSpacesAll, securitySolutionOnlyReadSpacesAll, observabilityOnlyAllSpacesAll, + logsOnlyAllSpacesAll, observabilityOnlyReadSpacesAll, + observabilityOnlyAllSpacesAllWithReadESIndices, observabilityMinReadAlertsRead, observabilityMinReadAlertsReadSpacesAll, observabilityMinimalRead, diff --git a/x-pack/test/rule_registry/common/lib/authentication/users.ts b/x-pack/test/rule_registry/common/lib/authentication/users.ts index 39f837c6df41d..be6021307e819 100644 --- a/x-pack/test/rule_registry/common/lib/authentication/users.ts +++ b/x-pack/test/rule_registry/common/lib/authentication/users.ts @@ -15,6 +15,7 @@ import { securitySolutionOnlyAllSpacesAll, securitySolutionOnlyReadSpacesAll, observabilityOnlyAllSpacesAll, + logsOnlyAllSpacesAll, observabilityOnlyReadSpacesAll, // trial license roles observabilityMinReadAlertsAll, @@ -27,6 +28,7 @@ import { observabilityOnlyAllSpace2, observabilityOnlyReadSpace2, observabilityMinReadAlertsAllSpacesAll, + observabilityOnlyAllSpacesAllWithReadESIndices, } from './roles'; import { User } from './types'; @@ -161,6 +163,18 @@ export const obsOnlySpacesAll: User = { roles: [observabilityOnlyAllSpacesAll.name], }; +export const logsOnlySpacesAll: User = { + username: 'logs_only_all_spaces_all', + password: 'logs_only_all_spaces_all', + roles: [logsOnlyAllSpacesAll.name], +}; + +export const obsOnlySpacesAllEsRead: User = { + username: 'obs_only_all_spaces_all_es_read', + password: 'obs_only_all_spaces_all_es_read', + roles: [observabilityOnlyAllSpacesAllWithReadESIndices.name], +}; + export const obsSecSpacesAll: User = { username: 'sec_only_all_spaces_all_and_obs_only_all_spaces_all', password: 'sec_only_all_spaces_all_and_obs_only_all_spaces_all', @@ -267,6 +281,7 @@ export const allUsers = [ secOnlySpacesAll, secOnlyReadSpacesAll, obsOnlySpacesAll, + logsOnlySpacesAll, obsSecSpacesAll, obsSecReadSpacesAll, obsMinReadAlertsRead, @@ -283,4 +298,5 @@ export const allUsers = [ obsOnlyReadSpace2, obsSecAllSpace2, obsSecReadSpace2, + obsOnlySpacesAllEsRead, ]; diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts index 2124cb8a1d04b..82c54a6907a04 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts @@ -20,27 +20,38 @@ import { } from '../../../../detection_engine_api_integration/utils'; import { ID } from '../../../../detection_engine_api_integration/security_and_spaces/tests/generating_signals'; import { QueryCreateSchema } from '../../../../../plugins/security_solution/common/detection_engine/schemas/request'; +import { + obsOnlySpacesAllEsRead, + obsOnlySpacesAll, + logsOnlySpacesAll, +} from '../../../common/lib/authentication/users'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - const bsearch = getService('bsearch'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + // const bsearch = getService('bsearch'); + const secureBsearch = getService('secureBsearch'); const log = getService('log'); const SPACE1 = 'space1'; describe('ruleRegistryAlertsSearchStrategy', () => { describe('logs', () => { - beforeEach(async () => { + before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); }); - afterEach(async () => { + after(async () => { await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); }); it('should return alerts from log rules', async () => { - const result = await bsearch.send({ - supertest, + const result = await secureBsearch.send({ + supertestWithoutAuth, + auth: { + username: logsOnlySpacesAll.username, + password: logsOnlySpacesAll.password, + }, options: { featureIds: [AlertConsumers.LOGS], }, @@ -54,25 +65,13 @@ export default ({ getService }: FtrProviderContext) => { }); }); + // TODO: need xavier's help here describe('siem', () => { - beforeEach(async () => { - await deleteSignalsIndex(supertest, log); - await createSignalsIndex(supertest, log); - }); - - afterEach(async () => { - await deleteSignalsIndex(supertest, log); - await deleteAllAlerts(supertest, log); - }); - before(async () => { + await createSignalsIndex(supertest, log); await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); - }); - after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); - }); + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); - it('should return alerts from siem rules', async () => { const rule: QueryCreateSchema = { ...getRuleForSignalTesting(['auditbeat-*']), query: `_id:${ID}`, @@ -80,9 +79,22 @@ export default ({ getService }: FtrProviderContext) => { const { id: createdId } = await createRule(supertest, log, rule); await waitForRuleSuccessOrStatus(supertest, log, createdId); await waitForSignalsToBePresent(supertest, log, 1, [createdId]); + }); - const result = await bsearch.send({ - supertest, + after(async () => { + await deleteSignalsIndex(supertest, log); + await deleteAllAlerts(supertest, log); + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + it('should return alerts from siem rules', async () => { + const result = await secureBsearch.send({ + supertestWithoutAuth, + auth: { + username: obsOnlySpacesAllEsRead.username, + password: obsOnlySpacesAllEsRead.password, + }, options: { featureIds: [AlertConsumers.SIEM], }, @@ -94,19 +106,45 @@ export default ({ getService }: FtrProviderContext) => { ); expect(consumers.every((consumer) => consumer === AlertConsumers.SIEM)); }); + + it('should throw an error when trying to to search for more than just siem', async () => { + type RuleRegistrySearchResponseWithErrors = RuleRegistrySearchResponse & { + statusCode: number; + message: string; + }; + const result = await secureBsearch.send({ + supertestWithoutAuth, + auth: { + username: obsOnlySpacesAllEsRead.username, + password: obsOnlySpacesAllEsRead.password, + }, + options: { + featureIds: [AlertConsumers.SIEM, AlertConsumers.LOGS], + }, + strategy: 'ruleRegistryAlertsSearchStrategy', + }); + expect(result.statusCode).to.be(500); + expect(result.message).to.be( + `The ruleRegistryAlertsSearchStrategy search strategy is unable to accommodate requests containing multiple feature IDs and one of those IDs is SIEM.` + ); + }); }); describe('apm', () => { - beforeEach(async () => { + before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/rule_registry/alerts'); }); - afterEach(async () => { + after(async () => { await esArchiver.unload('x-pack/test/functional/es_archives/rule_registry/alerts'); }); it('should return alerts from apm rules', async () => { - const result = await bsearch.send({ - supertest, + const result = await secureBsearch.send({ + supertestWithoutAuth, + auth: { + username: obsOnlySpacesAll.username, + password: obsOnlySpacesAll.password, + }, options: { featureIds: [AlertConsumers.APM], }, @@ -123,13 +161,16 @@ export default ({ getService }: FtrProviderContext) => { describe('empty response', () => { it('should return an empty response', async () => { - const result = await bsearch.send({ - supertest, + const result = await secureBsearch.send({ + supertestWithoutAuth, + auth: { + username: obsOnlySpacesAll.username, + password: obsOnlySpacesAll.password, + }, options: { featureIds: [], }, strategy: 'ruleRegistryAlertsSearchStrategy', - space: SPACE1, }); expect(result.rawResponse).to.eql({}); }); From ca447c66ce9c03b9ee1b32ca1b1cc671e583ee92 Mon Sep 17 00:00:00 2001 From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com> Date: Wed, 9 Mar 2022 14:00:17 -0500 Subject: [PATCH 134/140] [Security Solution] Adds posix eventing model fields to generator script (#127207) * Adds posix eventing model fields to generator script * Fix types and update snapshot --- .../common/endpoint/generate_data.test.ts | 12 ++ .../common/endpoint/generate_data.ts | 105 +++++++++++++++++- .../common/endpoint/types/generator.ts | 1 + .../common/endpoint/types/index.ts | 16 +++ .../isometric_taxi_layout.test.ts.snap | 88 +++++++-------- .../routes/resolver/utils/pagination.test.ts | 4 +- 6 files changed, 178 insertions(+), 48 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts index 301a032fb47df..6d750ae3c6670 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts @@ -578,4 +578,16 @@ describe('data generator', () => { const visitedEvents = countResolverEvents(rootNode, alertAncestors + generations); expect(visitedEvents).toEqual(events.length); }); + + it('creates full resolver tree with a single entry_leader id', () => { + const events = [...generator.alertsGenerator(1)]; + const [rootEvent, ...children] = events; + expect( + children.every((event) => { + return ( + event.process?.entry_leader?.entity_id === rootEvent.process?.entry_leader?.entity_id + ); + }) + ).toBe(true); + }); }); diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index f9ecb2e018dff..443b27802c538 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -346,6 +346,7 @@ export interface TreeOptions { ancestryArraySize?: number; eventsDataStream?: DataStream; alertsDataStream?: DataStream; + sessionEntryLeader?: string; } type TreeOptionDefaults = Required; @@ -363,6 +364,7 @@ export function getTreeOptionsWithDef(options?: TreeOptions): TreeOptionDefaults relatedEvents: options?.relatedEvents ?? 5, relatedEventsOrdered: options?.relatedEventsOrdered ?? false, relatedAlerts: options?.relatedAlerts ?? 3, + sessionEntryLeader: options?.sessionEntryLeader ?? '', percentWithRelated: options?.percentWithRelated ?? 30, percentTerminated: options?.percentTerminated ?? 100, alwaysGenMaxChildrenPerNode: options?.alwaysGenMaxChildrenPerNode ?? false, @@ -535,12 +537,14 @@ export class EndpointDocGenerator extends BaseDataGenerator { */ public generateMalwareAlert({ ts = new Date().getTime(), + sessionEntryLeader = this.randomString(10), entityID = this.randomString(10), parentEntityID, ancestry = [], alertsDataStream = alertsDefaultDataStream, }: { ts?: number; + sessionEntryLeader?: string; entityID?: string; parentEntityID?: string; ancestry?: string[]; @@ -608,6 +612,21 @@ export class EndpointDocGenerator extends BaseDataGenerator { sha1: 'fake sha1', sha256: 'fake sha256', }, + entry_leader: { + entity_id: sessionEntryLeader, + name: 'fake entry', + pid: Math.floor(Math.random() * 1000), + }, + session_leader: { + entity_id: sessionEntryLeader, + name: 'fake session', + pid: Math.floor(Math.random() * 1000), + }, + group_leader: { + entity_id: sessionEntryLeader, + name: 'fake leader', + pid: Math.floor(Math.random() * 1000), + }, Ext: { ancestry, code_signature: [ @@ -648,6 +667,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { */ public generateMemoryAlert({ ts = new Date().getTime(), + sessionEntryLeader = this.randomString(10), entityID = this.randomString(10), parentEntityID, ancestry = [], @@ -655,6 +675,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { alertType, }: { ts?: number; + sessionEntryLeader?: string; entityID?: string; parentEntityID?: string; ancestry?: string[]; @@ -701,6 +722,21 @@ export class EndpointDocGenerator extends BaseDataGenerator { sha1: 'fake sha1', sha256: 'fake sha256', }, + entry_leader: { + entity_id: sessionEntryLeader, + name: 'fake entry', + pid: Math.floor(Math.random() * 1000), + }, + session_leader: { + entity_id: sessionEntryLeader, + name: 'fake session', + pid: Math.floor(Math.random() * 1000), + }, + group_leader: { + entity_id: sessionEntryLeader, + name: 'fake leader', + pid: Math.floor(Math.random() * 1000), + }, Ext: { ancestry, code_signature: [ @@ -758,12 +794,14 @@ export class EndpointDocGenerator extends BaseDataGenerator { public generateAlert({ ts = new Date().getTime(), entityID = this.randomString(10), + sessionEntryLeader, parentEntityID, ancestry = [], alertsDataStream = alertsDefaultDataStream, }: { ts?: number; entityID?: string; + sessionEntryLeader?: string; parentEntityID?: string; ancestry?: string[]; alertsDataStream?: DataStream; @@ -773,6 +811,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { case AlertTypes.MALWARE: return this.generateMalwareAlert({ ts, + sessionEntryLeader, entityID, parentEntityID, ancestry, @@ -783,6 +822,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { return this.generateMemoryAlert({ ts, entityID, + sessionEntryLeader, parentEntityID, ancestry, alertsDataStream, @@ -791,6 +831,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { case AlertTypes.BEHAVIOR: return this.generateBehaviorAlert({ ts, + sessionEntryLeader, entityID, parentEntityID, ancestry, @@ -811,12 +852,14 @@ export class EndpointDocGenerator extends BaseDataGenerator { */ public generateBehaviorAlert({ ts = new Date().getTime(), + sessionEntryLeader = this.randomString(10), entityID = this.randomString(10), parentEntityID, ancestry = [], alertsDataStream = alertsDefaultDataStream, }: { ts?: number; + sessionEntryLeader?: string; entityID?: string; parentEntityID?: string; ancestry?: string[]; @@ -878,6 +921,21 @@ export class EndpointDocGenerator extends BaseDataGenerator { status: 'trusted', subject_name: 'Microsoft Windows', }, + entry_leader: { + entity_id: sessionEntryLeader, + name: 'fake entry', + pid: Math.floor(Math.random() * 1000), + }, + session_leader: { + entity_id: sessionEntryLeader, + name: 'fake session', + pid: Math.floor(Math.random() * 1000), + }, + group_leader: { + entity_id: sessionEntryLeader, + name: 'fake leader', + pid: Math.floor(Math.random() * 1000), + }, parent: parentEntityID ? { entity_id: parentEntityID, @@ -950,6 +1008,10 @@ export class EndpointDocGenerator extends BaseDataGenerator { options.ancestry?.slice(0, options?.ancestryArrayLimit ?? ANCESTRY_LIMIT) ?? []; const processName = options.processName ? options.processName : this.randomProcessName(); + const sessionEntryLeader = options.sessionEntryLeader + ? options.sessionEntryLeader + : this.randomString(10); + const userName = this.randomString(10); const detailRecordForEventType = options.extensions || ((eventCategory) => { @@ -992,13 +1054,29 @@ export class EndpointDocGenerator extends BaseDataGenerator { pid: 'pid' in options && typeof options.pid !== 'undefined' ? options.pid : this.randomN(5000), executable: `C:\\${processName}`, - args: `"C:\\${processName}" \\${this.randomString(3)}`, + args: [`"C:\\${processName}"`, `--${this.randomString(3)}`], + working_directory: `/home/${userName}/`, code_signature: { status: 'trusted', subject_name: 'Microsoft', }, hash: { md5: this.seededUUIDv4() }, entity_id: options.entityID ? options.entityID : this.randomString(10), + entry_leader: { + entity_id: sessionEntryLeader, + name: 'fake entry', + pid: Math.floor(Math.random() * 1000), + }, + session_leader: { + entity_id: sessionEntryLeader, + name: 'fake session', + pid: Math.floor(Math.random() * 1000), + }, + group_leader: { + entity_id: sessionEntryLeader, + name: 'fake leader', + pid: Math.floor(Math.random() * 1000), + }, parent: options.parentEntityID ? { entity_id: options.parentEntityID, @@ -1017,7 +1095,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { }, user: { domain: this.randomString(10), - name: this.randomString(10), + name: userName, }, }; } @@ -1179,7 +1257,9 @@ export class EndpointDocGenerator extends BaseDataGenerator { public *alertsGenerator(numAlerts: number, options: TreeOptions = {}) { const opts = getTreeOptionsWithDef(options); for (let i = 0; i < numAlerts; i++) { - yield* this.fullResolverTreeGenerator(opts); + // 1 session per resolver tree + const sessionEntryLeader = this.randomString(10); + yield* this.fullResolverTreeGenerator({ ...opts, sessionEntryLeader }); } } @@ -1222,8 +1302,11 @@ export class EndpointDocGenerator extends BaseDataGenerator { const events = []; const startDate = new Date().getTime(); + const root = this.generateEvent({ timestamp: startDate + 1000, + entityID: opts.sessionEntryLeader, + sessionEntryLeader: opts.sessionEntryLeader, eventsDataStream: opts.eventsDataStream, }); events.push(root); @@ -1240,6 +1323,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { node, relatedAlerts: alertsPerNode, alertCreationTime: secBeforeAlert, + sessionEntryLeader: opts.sessionEntryLeader, alertsDataStream: opts.alertsDataStream, })) { eventList.push(relatedAlert); @@ -1252,6 +1336,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { relatedEvents: opts.relatedEvents, processDuration: secBeforeEvent, ordered: opts.relatedEventsOrdered, + sessionEntryLeader: opts.sessionEntryLeader, eventsDataStream: opts.eventsDataStream, })) { eventList.push(relatedEvent); @@ -1273,6 +1358,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { timestamp: timestamp + termProcessDuration * 1000, entityID: entityIDSafeVersion(root), parentEntityID: parentEntityIDSafeVersion(root), + sessionEntryLeader: opts.sessionEntryLeader, eventCategory: ['process'], eventType: ['end'], eventsDataStream: opts.eventsDataStream, @@ -1292,6 +1378,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { ancestor = this.generateEvent({ timestamp, parentEntityID: entityIDSafeVersion(ancestor), + sessionEntryLeader: opts.sessionEntryLeader, // add the parent to the ancestry array ancestry, ancestryArrayLimit: opts.ancestryArraySize, @@ -1309,6 +1396,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { timestamp: timestamp + termProcessDuration * 1000, entityID: entityIDSafeVersion(ancestor), parentEntityID: parentEntityIDSafeVersion(ancestor), + sessionEntryLeader: opts.sessionEntryLeader, eventCategory: ['process'], eventType: ['end'], ancestry: ancestryArray(ancestor), @@ -1339,6 +1427,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { ts: timestamp, entityID: entityIDSafeVersion(ancestor), parentEntityID: parentEntityIDSafeVersion(ancestor), + sessionEntryLeader: opts.sessionEntryLeader, ancestry: ancestryArray(ancestor), alertsDataStream: opts.alertsDataStream, }) @@ -1395,6 +1484,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { const child = this.generateEvent({ timestamp, parentEntityID: currentStateEntityID, + sessionEntryLeader: opts.sessionEntryLeader, ancestry, ancestryArrayLimit: opts.ancestryArraySize, eventsDataStream: opts.eventsDataStream, @@ -1416,6 +1506,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { yield this.generateEvent({ timestamp: timestamp + processDuration * 1000, entityID: entityIDSafeVersion(child), + sessionEntryLeader: opts.sessionEntryLeader, parentEntityID: parentEntityIDSafeVersion(child), eventCategory: ['process'], eventType: ['end'], @@ -1428,6 +1519,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { yield* this.relatedEventsGenerator({ node: child, relatedEvents: opts.relatedEvents, + sessionEntryLeader: opts.sessionEntryLeader, processDuration, ordered: opts.relatedEventsOrdered, eventsDataStream: opts.eventsDataStream, @@ -1435,6 +1527,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { yield* this.relatedAlertsGenerator({ node: child, relatedAlerts: opts.relatedAlerts, + sessionEntryLeader: opts.sessionEntryLeader, alertCreationTime: processDuration, alertsDataStream: opts.alertsDataStream, }); @@ -1456,11 +1549,13 @@ export class EndpointDocGenerator extends BaseDataGenerator { relatedEvents = 10, processDuration = 6 * 3600, ordered = false, + sessionEntryLeader, eventsDataStream = eventsDefaultDataStream, }: { node: Event; relatedEvents?: RelatedEventInfo[] | number; processDuration?: number; + sessionEntryLeader: string; ordered?: boolean; eventsDataStream?: DataStream; }) { @@ -1491,6 +1586,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { yield this.generateEvent({ timestamp: ts, entityID: entityIDSafeVersion(node), + sessionEntryLeader, parentEntityID: parentEntityIDSafeVersion(node), eventCategory: eventInfo.category, eventType: eventInfo.creationType, @@ -1512,17 +1608,20 @@ export class EndpointDocGenerator extends BaseDataGenerator { relatedAlerts = 3, alertCreationTime = 6 * 3600, alertsDataStream = alertsDefaultDataStream, + sessionEntryLeader, }: { node: Event; relatedAlerts: number; alertCreationTime: number; alertsDataStream: DataStream; + sessionEntryLeader: string; }) { for (let i = 0; i < relatedAlerts; i++) { const ts = (timestampSafeVersion(node) ?? 0) + this.randomN(alertCreationTime) * 1000; yield this.generateAlert({ ts, entityID: entityIDSafeVersion(node), + sessionEntryLeader, parentEntityID: parentEntityIDSafeVersion(node), ancestry: ancestryArray(node), alertsDataStream, diff --git a/x-pack/plugins/security_solution/common/endpoint/types/generator.ts b/x-pack/plugins/security_solution/common/endpoint/types/generator.ts index ba900cfcb38ff..b512697fb5d01 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/generator.ts @@ -14,6 +14,7 @@ export interface EventOptions { timestamp?: number; entityID?: string; parentEntityID?: string; + sessionEntryLeader?: string; eventType?: string | string[]; eventCategory?: string | string[]; processName?: string; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 1fce6f17bdea6..6e6095a990a1f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -739,11 +739,27 @@ export type SafeEndpointEvent = Partial<{ }>; pid: ECSField; hash: Hashes; + working_directory: ECSField; parent: Partial<{ entity_id: ECSField; name: ECSField; pid: ECSField; }>; + session_leader: Partial<{ + entity_id: ECSField; + name: ECSField; + pid: ECSField; + }>; + entry_leader: Partial<{ + entity_id: ECSField; + name: ECSField; + pid: ECSField; + }>; + group_leader: Partial<{ + entity_id: ECSField; + name: ECSField; + pid: ECSField; + }>; /* * The array has a special format. The entity_ids towards the beginning of the array are closer ancestors and the * values towards the end of the array are more distant ancestors (grandparents). Therefore diff --git a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap index 1eda3a0980191..dca100efcc835 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap +++ b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap @@ -15,11 +15,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "lsass.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "lsass.exe", + "name": "powershell.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -33,11 +33,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "lsass.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "lsass.exe", + "name": "powershell.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -58,11 +58,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "notepad.exe", + "process.name": "lsass.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "notepad.exe", + "name": "lsass.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -73,11 +73,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "mimikatz.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "mimikatz.exe", + "name": "powershell.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -88,11 +88,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "C", - "process.name": "notepad.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "A", }, "id": "C", - "name": "notepad.exe", + "name": "iexlorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -103,11 +103,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "I", - "process.name": "mimikatz.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "A", }, "id": "I", - "name": "mimikatz.exe", + "name": "explorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -118,11 +118,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "D", - "process.name": "powershell.exe", + "process.name": "mimikatz.exe", "process.parent.entity_id": "B", }, "id": "D", - "name": "powershell.exe", + "name": "mimikatz.exe", "parent": "B", "stats": Object { "byCategory": Object {}, @@ -148,11 +148,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "F", - "process.name": "iexlorer.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "C", }, "id": "F", - "name": "iexlorer.exe", + "name": "explorer.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -163,11 +163,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "G", - "process.name": "iexlorer.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "C", }, "id": "G", - "name": "iexlorer.exe", + "name": "powershell.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -178,11 +178,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "H", - "process.name": "iexlorer.exe", + "process.name": "lsass.exe", "process.parent.entity_id": "G", }, "id": "H", - "name": "iexlorer.exe", + "name": "lsass.exe", "parent": "G", "stats": Object { "byCategory": Object {}, @@ -439,11 +439,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "notepad.exe", + "process.name": "lsass.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "notepad.exe", + "name": "lsass.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -457,11 +457,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "mimikatz.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "mimikatz.exe", + "name": "powershell.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -475,11 +475,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "C", - "process.name": "notepad.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "A", }, "id": "C", - "name": "notepad.exe", + "name": "iexlorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -493,11 +493,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "I", - "process.name": "mimikatz.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "A", }, "id": "I", - "name": "mimikatz.exe", + "name": "explorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -511,11 +511,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "D", - "process.name": "powershell.exe", + "process.name": "mimikatz.exe", "process.parent.entity_id": "B", }, "id": "D", - "name": "powershell.exe", + "name": "mimikatz.exe", "parent": "B", "stats": Object { "byCategory": Object {}, @@ -547,11 +547,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "F", - "process.name": "iexlorer.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "C", }, "id": "F", - "name": "iexlorer.exe", + "name": "explorer.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -565,11 +565,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "G", - "process.name": "iexlorer.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "C", }, "id": "G", - "name": "iexlorer.exe", + "name": "powershell.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -583,11 +583,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "H", - "process.name": "iexlorer.exe", + "process.name": "lsass.exe", "process.parent.entity_id": "G", }, "id": "H", - "name": "iexlorer.exe", + "name": "lsass.exe", "parent": "G", "stats": Object { "byCategory": Object {}, @@ -608,11 +608,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "powershell.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "powershell.exe", + "name": "explorer.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -623,11 +623,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "powershell.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "powershell.exe", + "name": "iexlorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -661,11 +661,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "powershell.exe", + "process.name": "explorer.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "powershell.exe", + "name": "explorer.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -679,11 +679,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "powershell.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "powershell.exe", + "name": "iexlorer.exe", "parent": "A", "stats": Object { "byCategory": Object {}, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.test.ts index c3182014cbb03..8be5ff5427192 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.test.ts @@ -22,7 +22,9 @@ describe('Pagination', () => { }; describe('cursor', () => { const root = generator.generateEvent(); - const events = Array.from(generator.relatedEventsGenerator({ node: root, relatedEvents: 5 })); + const events = Array.from( + generator.relatedEventsGenerator({ node: root, relatedEvents: 5, sessionEntryLeader: 'test' }) + ); it('does build a cursor when received the same number of events as was requested', () => { expect(PaginationBuilder.buildCursorRequestLimit(4, events)).not.toBeNull(); From b2cd94df7b8e36e167e7d305def462b1ae1d70cb Mon Sep 17 00:00:00 2001 From: Hannah Mudge Date: Wed, 9 Mar 2022 12:04:27 -0700 Subject: [PATCH 135/140] [Controls] Improve controls empty state (#125728) * Add controls button to toolbar * Add dismiss button * Add style to toolbar controls button * Clean up unnecessary isControlsEnabled check * Make toolbar controls button conditional once callout dismissed * Move add and edit controls to toolbar dropdown * Remove icon buttons * Add each control seperately to toolbar dropdown * Remove unused code * Fix close popover on click * Remove unnecessary dark theme check * Make closePopover optional for creating controls * Fix control group strings * Fix alignment of toolbar popover items * Functional tests - create controls from new menu button * Hide controls callout for empty dashboards * Add tooltips to control types + i18n support. * Move callout render logic to dashboard viewport * Add controls callout functional tests * Fix bundle size by lazy importing controls callout * Get create control button in callout via passed function * Fix mobile view of callout * Add documentation and cleaned code based on Devon's feedback * Moved the 'add to library' and 'controls' buttons in to extra --- .../component/control_group_component.tsx | 184 +++++---------- .../control_group/control_group_strings.ts | 12 +- .../editor/control_group_editor.tsx | 153 ++++++++++++ .../control_group/editor/create_control.tsx | 162 +++++++------ .../editor/edit_control_group.tsx | 222 ++++++------------ .../control_group/editor/editor_constants.ts | 3 +- .../embeddable/control_group_container.tsx | 77 ++++++ .../state/control_group_reducers.ts | 6 - .../options_list_embeddable_factory.tsx | 5 +- .../options_list/options_list_strings.ts | 8 + .../controls_callout/controls_callout.tsx | 69 ++++++ .../controls_illustration.scss | 0 .../controls_illustration.tsx | 0 .../controls/public/controls_callout/index.ts | 11 + src/plugins/controls/public/index.ts | 2 + .../viewport/_dashboard_viewport.scss | 4 +- .../viewport/dashboard_viewport.tsx | 23 +- .../application/top_nav/dashboard_top_nav.tsx | 11 +- .../solution_toolbar/solution_toolbar.tsx | 16 +- .../dashboard_controls_integration.ts | 36 ++- .../page_objects/dashboard_page_controls.ts | 6 +- .../workpad_header.component.tsx | 8 +- 22 files changed, 621 insertions(+), 397 deletions(-) create mode 100644 src/plugins/controls/public/control_group/editor/control_group_editor.tsx create mode 100644 src/plugins/controls/public/controls_callout/controls_callout.tsx rename src/plugins/controls/public/{control_group/component => controls_callout}/controls_illustration.scss (100%) rename src/plugins/controls/public/{control_group/component => controls_callout}/controls_illustration.tsx (100%) create mode 100644 src/plugins/controls/public/controls_callout/index.ts diff --git a/src/plugins/controls/public/control_group/component/control_group_component.tsx b/src/plugins/controls/public/control_group/component/control_group_component.tsx index 93dd6bf81e298..f7b87ce7be9f7 100644 --- a/src/plugins/controls/public/control_group/component/control_group_component.tsx +++ b/src/plugins/controls/public/control_group/component/control_group_component.tsx @@ -8,14 +8,7 @@ import '../control_group.scss'; -import { - EuiButtonIcon, - EuiFlexGroup, - EuiFlexItem, - EuiToolTip, - EuiPanel, - EuiText, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import React, { useMemo, useState } from 'react'; import classNames from 'classnames'; import { @@ -35,24 +28,13 @@ import { useSensors, LayoutMeasuringStrategy, } from '@dnd-kit/core'; - import { ControlGroupInput } from '../types'; -import { pluginServices } from '../../services'; import { ViewMode } from '../../../../embeddable/public'; -import { ControlGroupStrings } from '../control_group_strings'; -import { CreateControlButton } from '../editor/create_control'; -import { EditControlGroup } from '../editor/edit_control_group'; -import { forwardAllContext } from '../editor/forward_all_context'; import { controlGroupReducers } from '../state/control_group_reducers'; import { ControlClone, SortableControl } from './control_group_sortable_item'; import { useReduxContainerContext } from '../../../../presentation_util/public'; -import { ControlsIllustration } from './controls_illustration'; export const ControlGroup = () => { - // Controls Services Context - const { overlays } = pluginServices.getHooks(); - const { openFlyout } = overlays.useService(); - // Redux embeddable container Context const reduxContainerContext = useReduxContainerContext< ControlGroupInput, @@ -114,115 +96,69 @@ export const ControlGroup = () => { if (draggingId) panelBg = 'success'; return ( - + <> {idsInOrder.length > 0 ? ( - - - setDraggingId(active.id)} - onDragEnd={onDragEnd} - onDragCancel={() => setDraggingId(null)} - sensors={sensors} - collisionDetection={closestCenter} - layoutMeasuring={{ - strategy: LayoutMeasuringStrategy.Always, - }} - > - - - {idsInOrder.map( - (controlId, index) => - panels[controlId] && ( - - ) - )} - - - - {draggingId ? : null} - - - - {isEditable && ( - - - - - { - const flyoutInstance = openFlyout( - forwardAllContext( - flyoutInstance.close()} />, - reduxContainerContext - ) - ); - }} - /> - - - - - - - - - - )} - - ) : ( - <> - - - - - - - - {' '} - -

{ControlGroupStrings.emptyState.getCallToAction()}

-
-
-
-
- -
- -
+ + + setDraggingId(active.id)} + onDragEnd={onDragEnd} + onDragCancel={() => setDraggingId(null)} + sensors={sensors} + collisionDetection={closestCenter} + layoutMeasuring={{ + strategy: LayoutMeasuringStrategy.Always, + }} + > + + + {idsInOrder.map( + (controlId, index) => + panels[controlId] && ( + + ) + )} + + + + {draggingId ? : null} + + - +
+ ) : ( + <> )} - + ); }; diff --git a/src/plugins/controls/public/control_group/control_group_strings.ts b/src/plugins/controls/public/control_group/control_group_strings.ts index 91e857d083f7f..73b81d8c5d459 100644 --- a/src/plugins/controls/public/control_group/control_group_strings.ts +++ b/src/plugins/controls/public/control_group/control_group_strings.ts @@ -13,6 +13,10 @@ export const ControlGroupStrings = { i18n.translate('controls.controlGroup.title', { defaultMessage: 'Control group', }), + getControlButtonTitle: () => + i18n.translate('controls.controlGroup.toolbarButtonTitle', { + defaultMessage: 'Controls', + }), emptyState: { getCallToAction: () => i18n.translate('controls.controlGroup.emptyState.callToAction', { @@ -26,6 +30,10 @@ export const ControlGroupStrings = { i18n.translate('controls.controlGroup.emptyState.twoLineLoadingTitle', { defaultMessage: '...', }), + getDismissButton: () => + i18n.translate('controls.controlGroup.emptyState.dismissButton', { + defaultMessage: 'Dismiss', + }), }, manageControl: { getFlyoutCreateTitle: () => @@ -60,11 +68,11 @@ export const ControlGroupStrings = { }), getManageButtonTitle: () => i18n.translate('controls.controlGroup.management.buttonTitle', { - defaultMessage: 'Configure controls', + defaultMessage: 'Settings', }), getFlyoutTitle: () => i18n.translate('controls.controlGroup.management.flyoutTitle', { - defaultMessage: 'Configure controls', + defaultMessage: 'Control settings', }), getDefaultWidthTitle: () => i18n.translate('controls.controlGroup.management.defaultWidthTitle', { diff --git a/src/plugins/controls/public/control_group/editor/control_group_editor.tsx b/src/plugins/controls/public/control_group/editor/control_group_editor.tsx new file mode 100644 index 0000000000000..6bb8b12d4f643 --- /dev/null +++ b/src/plugins/controls/public/control_group/editor/control_group_editor.tsx @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import { + EuiFlyoutHeader, + EuiButtonGroup, + EuiFlyoutBody, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiFlyoutFooter, + EuiButton, + EuiFormRow, + EuiButtonEmpty, + EuiSpacer, + EuiCheckbox, +} from '@elastic/eui'; + +import { ControlGroupStrings } from '../control_group_strings'; +import { ControlStyle, ControlWidth } from '../../types'; +import { CONTROL_LAYOUT_OPTIONS, CONTROL_WIDTH_OPTIONS } from './editor_constants'; + +interface EditControlGroupProps { + width: ControlWidth; + controlStyle: ControlStyle; + setAllWidths: boolean; + updateControlStyle: (controlStyle: ControlStyle) => void; + updateWidth: (newWidth: ControlWidth) => void; + updateAllControlWidths: (newWidth: ControlWidth) => void; + onCancel: () => void; + onClose: () => void; +} + +export const ControlGroupEditor = ({ + width, + controlStyle, + setAllWidths, + updateControlStyle, + updateWidth, + updateAllControlWidths, + onCancel, + onClose, +}: EditControlGroupProps) => { + const [currentControlStyle, setCurrentControlStyle] = useState(controlStyle); + const [currentWidth, setCurrentWidth] = useState(width); + const [applyToAll, setApplyToAll] = useState(setAllWidths); + + return ( + <> + + +

{ControlGroupStrings.management.getFlyoutTitle()}

+
+
+ + + { + setCurrentControlStyle(newControlStyle as ControlStyle); + }} + /> + + + + { + setCurrentWidth(newWidth as ControlWidth); + }} + /> + + + { + setApplyToAll(e.target.checked); + }} + /> + + + + {ControlGroupStrings.management.getDeleteAllButtonTitle()} + + + + + + { + onClose(); + }} + > + {ControlGroupStrings.manageControl.getCancelTitle()} + + + + { + if (currentControlStyle && currentControlStyle !== controlStyle) { + updateControlStyle(currentControlStyle); + } + if (currentWidth && currentWidth !== width) { + updateWidth(currentWidth); + } + if (applyToAll) { + updateAllControlWidths(currentWidth); + } + onClose(); + }} + > + {ControlGroupStrings.manageControl.getSaveChangesTitle()} + + + + + + ); +}; diff --git a/src/plugins/controls/public/control_group/editor/create_control.tsx b/src/plugins/controls/public/control_group/editor/create_control.tsx index b97ebb9aa519b..57e74d7c1b5d7 100644 --- a/src/plugins/controls/public/control_group/editor/create_control.tsx +++ b/src/plugins/controls/public/control_group/editor/create_control.tsx @@ -8,7 +8,6 @@ import { EuiButton, - EuiButtonIcon, EuiButtonIconColor, EuiContextMenuItem, EuiContextMenuPanel, @@ -16,42 +15,39 @@ import { } from '@elastic/eui'; import React, { useState, ReactElement } from 'react'; -import { ControlGroupInput } from '../types'; import { pluginServices } from '../../services'; import { ControlEditor } from './control_editor'; import { OverlayRef } from '../../../../../core/public'; -import { forwardAllContext } from './forward_all_context'; import { DEFAULT_CONTROL_WIDTH } from './editor_constants'; import { ControlGroupStrings } from '../control_group_strings'; -import { controlGroupReducers } from '../state/control_group_reducers'; import { EmbeddableFactoryNotFoundError } from '../../../../embeddable/public'; -import { useReduxContainerContext } from '../../../../presentation_util/public'; import { ControlWidth, IEditableControlFactory, ControlInput } from '../../types'; - -export const CreateControlButton = ({ isIconButton }: { isIconButton: boolean }) => { +import { toMountPoint } from '../../../../kibana_react/public'; + +export type CreateControlButtonTypes = 'toolbar' | 'callout'; +export interface CreateControlButtonProps { + defaultControlWidth?: ControlWidth; + updateDefaultWidth: (defaultControlWidth: ControlWidth) => void; + addNewEmbeddable: (type: string, input: Omit) => void; + buttonType: CreateControlButtonTypes; + closePopover?: () => void; +} + +export const CreateControlButton = ({ + defaultControlWidth, + updateDefaultWidth, + addNewEmbeddable, + buttonType, + closePopover, +}: CreateControlButtonProps) => { // Controls Services Context - const { overlays, controls } = pluginServices.getHooks(); - const { getControlTypes, getControlFactory } = controls.useService(); - const { openFlyout, openConfirm } = overlays.useService(); - - // Redux embeddable container Context - const reduxContainerContext = useReduxContainerContext< - ControlGroupInput, - typeof controlGroupReducers - >(); - const { - containerActions: { addNewEmbeddable }, - actions: { setDefaultControlWidth }, - useEmbeddableSelector, - useEmbeddableDispatch, - } = reduxContainerContext; - const dispatch = useEmbeddableDispatch(); - - // current state - const { defaultControlWidth } = useEmbeddableSelector((state) => state); + const { overlays, controls } = pluginServices.getServices(); + const { getControlTypes, getControlFactory } = controls; + const { openFlyout, openConfirm } = overlays; const [isControlTypePopoverOpen, setIsControlTypePopoverOpen] = useState(false); const createNewControl = async (type: string) => { + const PresentationUtilProvider = pluginServices.getContextProvider(); const factory = getControlFactory(type); if (!factory) throw new EmbeddableFactoryNotFoundError(type); @@ -80,26 +76,27 @@ export const CreateControlButton = ({ isIconButton }: { isIconButton: boolean }) const editableFactory = factory as IEditableControlFactory; const flyoutInstance = openFlyout( - forwardAllContext( - (inputToReturn.title = newTitle)} - updateWidth={(newWidth) => dispatch(setDefaultControlWidth(newWidth as ControlWidth))} - onTypeEditorChange={(partialInput) => - (inputToReturn = { ...inputToReturn, ...partialInput }) - } - onSave={() => { - if (editableFactory.presaveTransformFunction) { - inputToReturn = editableFactory.presaveTransformFunction(inputToReturn); + toMountPoint( + + (inputToReturn.title = newTitle)} + updateWidth={updateDefaultWidth} + onTypeEditorChange={(partialInput) => + (inputToReturn = { ...inputToReturn, ...partialInput }) } - resolve(inputToReturn); - flyoutInstance.close(); - }} - onCancel={() => onCancel(flyoutInstance)} - />, - reduxContainerContext + onSave={() => { + if (editableFactory.presaveTransformFunction) { + inputToReturn = editableFactory.presaveTransformFunction(inputToReturn); + } + resolve(inputToReturn); + flyoutInstance.close(); + }} + onCancel={() => onCancel(flyoutInstance)} + /> + ), { onClose: (flyout) => onCancel(flyout), @@ -117,48 +114,49 @@ export const CreateControlButton = ({ isIconButton }: { isIconButton: boolean }) if (getControlTypes().length === 0) return null; - const onCreateButtonClick = () => { - if (getControlTypes().length > 1) { - setIsControlTypePopoverOpen(!isControlTypePopoverOpen); - return; - } - createNewControl(getControlTypes()[0]); - }; - const commonButtonProps = { - onClick: onCreateButtonClick, color: 'primary' as EuiButtonIconColor, 'data-test-subj': 'controls-create-button', 'aria-label': ControlGroupStrings.management.getManageButtonTitle(), }; - const createControlButton = isIconButton ? ( - - ) : ( - - {ControlGroupStrings.emptyState.getAddControlButtonTitle()} - - ); - - if (getControlTypes().length > 1) { - const items: ReactElement[] = []; - getControlTypes().forEach((type) => { - const factory = getControlFactory(type); - items.push( - { + const items: ReactElement[] = []; + getControlTypes().forEach((type) => { + const factory = getControlFactory(type); + items.push( + { + if (buttonType === 'callout' && isControlTypePopoverOpen) { setIsControlTypePopoverOpen(false); - createNewControl(type); - }} - > - {factory.getDisplayName()} - - ); - }); - + } else if (closePopover) { + closePopover(); + } + createNewControl(type); + }} + toolTipContent={factory.getDescription()} + > + {factory.getDisplayName()} + + ); + }); + + if (buttonType === 'callout') { + const onCreateButtonClick = () => { + if (getControlTypes().length > 1) { + setIsControlTypePopoverOpen(!isControlTypePopoverOpen); + return; + } + createNewControl(getControlTypes()[0]); + }; + + const createControlButton = ( + + {ControlGroupStrings.emptyState.getAddControlButtonTitle()} + + ); return ( setIsControlTypePopoverOpen(false)} > - + ); } - return createControlButton; + return ; }; diff --git a/src/plugins/controls/public/control_group/editor/edit_control_group.tsx b/src/plugins/controls/public/control_group/editor/edit_control_group.tsx index 87a2a1407a761..57a5fb22b9d9f 100644 --- a/src/plugins/controls/public/control_group/editor/edit_control_group.tsx +++ b/src/plugins/controls/public/control_group/editor/edit_control_group.tsx @@ -6,165 +6,93 @@ * Side Public License, v 1. */ -import React, { useState } from 'react'; -import { - EuiTitle, - EuiSpacer, - EuiFormRow, - EuiFlexItem, - EuiFlexGroup, - EuiFlyoutBody, - EuiButtonGroup, - EuiButtonEmpty, - EuiFlyoutHeader, - EuiCheckbox, - EuiFlyoutFooter, - EuiButton, -} from '@elastic/eui'; +import React from 'react'; +import { EuiContextMenuItem } from '@elastic/eui'; -import { - CONTROL_LAYOUT_OPTIONS, - CONTROL_WIDTH_OPTIONS, - DEFAULT_CONTROL_WIDTH, -} from './editor_constants'; -import { ControlGroupInput } from '../types'; +import { DEFAULT_CONTROL_WIDTH, DEFAULT_CONTROL_STYLE } from './editor_constants'; +import { ControlsPanels } from '../types'; import { pluginServices } from '../../services'; import { ControlStyle, ControlWidth } from '../../types'; import { ControlGroupStrings } from '../control_group_strings'; -import { controlGroupReducers } from '../state/control_group_reducers'; -import { useReduxContainerContext } from '../../../../presentation_util/public'; +import { toMountPoint } from '../../../../kibana_react/public'; +import { OverlayRef } from '../../../../../core/public'; +import { ControlGroupEditor } from './control_group_editor'; -interface EditControlGroupState { - newControlStyle: ControlGroupInput['controlStyle']; - newDefaultWidth: ControlGroupInput['defaultControlWidth']; - setAllWidths: boolean; +export interface EditControlGroupButtonProps { + controlStyle: ControlStyle; + panels?: ControlsPanels; + defaultControlWidth?: ControlWidth; + setControlStyle: (setControlStyle: ControlStyle) => void; + setDefaultControlWidth: (defaultControlWidth: ControlWidth) => void; + setAllControlWidths: (defaultControlWidth: ControlWidth) => void; + removeEmbeddable?: (panelId: string) => void; + closePopover: () => void; } -export const EditControlGroup = ({ closeFlyout }: { closeFlyout: () => void }) => { - const { overlays } = pluginServices.getHooks(); - const { openConfirm } = overlays.useService(); +export const EditControlGroup = ({ + panels, + defaultControlWidth, + controlStyle, + setControlStyle, + setDefaultControlWidth, + setAllControlWidths, + removeEmbeddable, + closePopover, +}: EditControlGroupButtonProps) => { + const { overlays } = pluginServices.getServices(); + const { openConfirm, openFlyout } = overlays; - const { - containerActions, - useEmbeddableSelector, - useEmbeddableDispatch, - actions: { setControlStyle, setAllControlWidths, setDefaultControlWidth }, - } = useReduxContainerContext(); - const dispatch = useEmbeddableDispatch(); - const { panels, controlStyle, defaultControlWidth } = useEmbeddableSelector((state) => state); + const editControlGroup = () => { + const PresentationUtilProvider = pluginServices.getContextProvider(); - const [state, setState] = useState({ - newControlStyle: controlStyle, - newDefaultWidth: defaultControlWidth, - setAllWidths: false, - }); + const onCancel = (ref: OverlayRef) => { + if (!removeEmbeddable || !panels) return; + openConfirm(ControlGroupStrings.management.deleteControls.getSubtitle(), { + confirmButtonText: ControlGroupStrings.management.deleteControls.getConfirm(), + cancelButtonText: ControlGroupStrings.management.deleteControls.getCancel(), + title: ControlGroupStrings.management.deleteControls.getDeleteAllTitle(), + buttonColor: 'danger', + }).then((confirmed) => { + if (confirmed) Object.keys(panels).forEach((panelId) => removeEmbeddable(panelId)); + ref.close(); + }); + }; - const onSave = () => { - const { newControlStyle, newDefaultWidth, setAllWidths } = state; - if (newControlStyle && newControlStyle !== controlStyle) { - dispatch(setControlStyle(newControlStyle)); - } - if (newDefaultWidth && newDefaultWidth !== defaultControlWidth) { - dispatch(setDefaultControlWidth(newDefaultWidth)); - } - if (setAllWidths && newDefaultWidth) { - dispatch(setAllControlWidths(newDefaultWidth)); - } - closeFlyout(); + const flyoutInstance = openFlyout( + toMountPoint( + + onCancel(flyoutInstance)} + onClose={() => flyoutInstance.close()} + /> + + ), + { + onClose: () => flyoutInstance.close(), + } + ); }; - return ( - <> - - -

{ControlGroupStrings.management.getFlyoutTitle()}

-
-
- - - - setState((s) => ({ ...s, newControlStyle: newControlStyle as ControlStyle })) - } - /> - - - - - setState((s) => ({ ...s, newDefaultWidth: newDefaultWidth as ControlWidth })) - } - /> - - - setState((s) => ({ ...s, setAllWidths: e.target.checked }))} - /> - + const commonButtonProps = { + key: 'manageControls', + onClick: () => { + editControlGroup(); + closePopover(); + }, + icon: 'gear', + 'data-test-subj': 'controls-sorting-button', + 'aria-label': ControlGroupStrings.management.getManageButtonTitle(), + }; - { - if (!containerActions?.removeEmbeddable) return; - closeFlyout(); - openConfirm(ControlGroupStrings.management.deleteControls.getSubtitle(), { - confirmButtonText: ControlGroupStrings.management.deleteControls.getConfirm(), - cancelButtonText: ControlGroupStrings.management.deleteControls.getCancel(), - title: ControlGroupStrings.management.deleteControls.getDeleteAllTitle(), - buttonColor: 'danger', - }).then((confirmed) => { - if (confirmed) - Object.keys(panels).forEach((panelId) => - containerActions.removeEmbeddable(panelId) - ); - }); - }} - aria-label={'delete-all'} - iconType="trash" - color="danger" - flush="left" - size="s" - > - {ControlGroupStrings.management.getDeleteAllButtonTitle()} - - - - - - { - closeFlyout(); - }} - > - {ControlGroupStrings.manageControl.getCancelTitle()} - - - - { - onSave(); - }} - > - {ControlGroupStrings.manageControl.getSaveChangesTitle()} - - - - - + return ( + + {ControlGroupStrings.management.getManageButtonTitle()} + ); }; diff --git a/src/plugins/controls/public/control_group/editor/editor_constants.ts b/src/plugins/controls/public/control_group/editor/editor_constants.ts index 814e2a08cd931..f68110a7f2b9d 100644 --- a/src/plugins/controls/public/control_group/editor/editor_constants.ts +++ b/src/plugins/controls/public/control_group/editor/editor_constants.ts @@ -6,10 +6,11 @@ * Side Public License, v 1. */ -import { ControlWidth } from '../../types'; +import { ControlStyle, ControlWidth } from '../../types'; import { ControlGroupStrings } from '../control_group_strings'; export const DEFAULT_CONTROL_WIDTH: ControlWidth = 'auto'; +export const DEFAULT_CONTROL_STYLE: ControlStyle = 'oneLine'; export const CONTROL_WIDTH_OPTIONS = [ { diff --git a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx index b420e026ac1d3..7393462caf029 100644 --- a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx @@ -13,6 +13,7 @@ import deepEqual from 'fast-deep-equal'; import { Filter, uniqFilters } from '@kbn/es-query'; import { EMPTY, merge, pipe, Subscription } from 'rxjs'; import { distinctUntilChanged, debounceTime, catchError, switchMap, map } from 'rxjs/operators'; +import { EuiContextMenuPanel, EuiHorizontalRule } from '@elastic/eui'; import { ControlGroupInput, @@ -24,6 +25,7 @@ import { withSuspense, LazyReduxEmbeddableWrapper, ReduxEmbeddableWrapperPropsWithChildren, + SolutionToolbarPopover, } from '../../../../presentation_util/public'; import { pluginServices } from '../../services'; import { DataView } from '../../../../data_views/public'; @@ -32,6 +34,9 @@ import { ControlGroup } from '../component/control_group_component'; import { controlGroupReducers } from '../state/control_group_reducers'; import { ControlEmbeddable, ControlInput, ControlOutput } from '../../types'; import { Container, EmbeddableFactory } from '../../../../embeddable/public'; +import { CreateControlButton, CreateControlButtonTypes } from '../editor/create_control'; +import { EditControlGroup } from '../editor/edit_control_group'; +import { ControlGroupStrings } from '../control_group_strings'; const ControlGroupReduxWrapper = withSuspense< ReduxEmbeddableWrapperPropsWithChildren @@ -63,6 +68,74 @@ export class ControlGroupContainer extends Container< return Promise.resolve(); }; + /** + * Returns a button that allows controls to be created externally using the embeddable + * @param buttonType Controls the button styling + * @param closePopover Closes the create control menu popover when flyout opens - only necessary if `buttonType === 'toolbar'` + * @return If `buttonType == 'toolbar'`, returns `EuiContextMenuPanel` with input control types as items. + * Otherwise, if `buttonType == 'callout'` returns `EuiButton` with popover containing input control types. + */ + public getCreateControlButton = ( + buttonType: CreateControlButtonTypes, + closePopover?: () => void + ) => { + return ( + this.updateInput({ defaultControlWidth })} + addNewEmbeddable={(type, input) => this.addNewEmbeddable(type, input)} + closePopover={closePopover} + /> + ); + }; + + private getEditControlGroupButton = (closePopover: () => void) => { + return ( + this.updateInput({ controlStyle })} + setDefaultControlWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })} + setAllControlWidths={(defaultControlWidth) => { + Object.keys(this.getInput().panels).forEach( + (panelId) => (this.getInput().panels[panelId].width = defaultControlWidth) + ); + }} + removeEmbeddable={(id) => this.removeEmbeddable(id)} + closePopover={closePopover} + /> + ); + }; + + /** + * Returns the toolbar button that is used for creating controls and managing control settings + * @return `SolutionToolbarPopover` button for input controls + */ + public getToolbarButtons = () => { + return ( + + {({ closePopover }: { closePopover: () => void }) => ( + , + this.getEditControlGroupButton(closePopover), + ]} + /> + )} + + ); + }; + constructor(initialInput: ControlGroupInput, parent?: Container) { super( initialInput, @@ -100,6 +173,10 @@ export class ControlGroupContainer extends Container< }); } + public getPanelCount = () => { + return Object.keys(this.getInput().panels).length; + }; + private recalculateOutput = () => { const allFilters: Filter[] = []; const allDataViews: DataView[] = []; diff --git a/src/plugins/controls/public/control_group/state/control_group_reducers.ts b/src/plugins/controls/public/control_group/state/control_group_reducers.ts index b7c0c62535d4c..5ec4463c3bc10 100644 --- a/src/plugins/controls/public/control_group/state/control_group_reducers.ts +++ b/src/plugins/controls/public/control_group/state/control_group_reducers.ts @@ -25,12 +25,6 @@ export const controlGroupReducers = { ) => { state.defaultControlWidth = action.payload; }, - setAllControlWidths: ( - state: WritableDraft, - action: PayloadAction - ) => { - Object.keys(state.panels).forEach((panelId) => (state.panels[panelId].width = action.payload)); - }, setControlWidth: ( state: WritableDraft, action: PayloadAction<{ width: ControlWidth; embeddableId: string }> diff --git a/src/plugins/controls/public/control_types/options_list/options_list_embeddable_factory.tsx b/src/plugins/controls/public/control_types/options_list/options_list_embeddable_factory.tsx index 98d616faadc58..8c6b533fa06e9 100644 --- a/src/plugins/controls/public/control_types/options_list/options_list_embeddable_factory.tsx +++ b/src/plugins/controls/public/control_types/options_list/options_list_embeddable_factory.tsx @@ -16,6 +16,7 @@ import { createOptionsListExtract, createOptionsListInject, } from '../../../common/control_types/options_list/options_list_persistable_state'; +import { OptionsListStrings } from './options_list_strings'; export class OptionsListEmbeddableFactory implements EmbeddableFactoryDefinition, IEditableControlFactory @@ -49,7 +50,9 @@ export class OptionsListEmbeddableFactory public isEditable = () => Promise.resolve(false); - public getDisplayName = () => 'Options List Control'; + public getDisplayName = () => OptionsListStrings.getDisplayName(); + public getIconType = () => 'list'; + public getDescription = () => OptionsListStrings.getDescription(); public inject = createOptionsListInject(); public extract = createOptionsListExtract(); diff --git a/src/plugins/controls/public/control_types/options_list/options_list_strings.ts b/src/plugins/controls/public/control_types/options_list/options_list_strings.ts index 537697804dd5b..c7074eb478a5f 100644 --- a/src/plugins/controls/public/control_types/options_list/options_list_strings.ts +++ b/src/plugins/controls/public/control_types/options_list/options_list_strings.ts @@ -9,6 +9,14 @@ import { i18n } from '@kbn/i18n'; export const OptionsListStrings = { + getDisplayName: () => + i18n.translate('controls.optionsList.displayName', { + defaultMessage: 'Options list', + }), + getDescription: () => + i18n.translate('controls.optionsList.description', { + defaultMessage: 'Add control that allows options to be selected from a dropdown.', + }), summary: { getSeparator: () => i18n.translate('controls.optionsList.summary.separator', { diff --git a/src/plugins/controls/public/controls_callout/controls_callout.tsx b/src/plugins/controls/public/controls_callout/controls_callout.tsx new file mode 100644 index 0000000000000..096d47b470a9d --- /dev/null +++ b/src/plugins/controls/public/controls_callout/controls_callout.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiButtonEmpty, EuiPanel } from '@elastic/eui'; +import React from 'react'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import classNames from 'classnames'; + +import { ControlGroupStrings } from '../control_group/control_group_strings'; +import { ControlsIllustration } from './controls_illustration'; + +const CONTROLS_CALLOUT_STATE_KEY = 'dashboard:controlsCalloutDismissed'; + +export interface CalloutProps { + getCreateControlButton?: () => JSX.Element; +} + +export const ControlsCallout = ({ getCreateControlButton }: CalloutProps) => { + const [controlsCalloutDismissed, setControlsCalloutDismissed] = useLocalStorage( + CONTROLS_CALLOUT_STATE_KEY, + false + ); + const dismissControls = () => { + setControlsCalloutDismissed(true); + }; + + if (controlsCalloutDismissed) return null; + + return ( + + + + + + + + + +

{ControlGroupStrings.emptyState.getCallToAction()}

+
+
+ {getCreateControlButton ? ( + {getCreateControlButton()} + ) : null} + + + {ControlGroupStrings.emptyState.getDismissButton()} + + +
+
+
+
+ ); +}; + +// required for dynamic import using React.lazy() +// eslint-disable-next-line import/no-default-export +export default ControlsCallout; diff --git a/src/plugins/controls/public/control_group/component/controls_illustration.scss b/src/plugins/controls/public/controls_callout/controls_illustration.scss similarity index 100% rename from src/plugins/controls/public/control_group/component/controls_illustration.scss rename to src/plugins/controls/public/controls_callout/controls_illustration.scss diff --git a/src/plugins/controls/public/control_group/component/controls_illustration.tsx b/src/plugins/controls/public/controls_callout/controls_illustration.tsx similarity index 100% rename from src/plugins/controls/public/control_group/component/controls_illustration.tsx rename to src/plugins/controls/public/controls_callout/controls_illustration.tsx diff --git a/src/plugins/controls/public/controls_callout/index.ts b/src/plugins/controls/public/controls_callout/index.ts new file mode 100644 index 0000000000000..5f6c7e5531522 --- /dev/null +++ b/src/plugins/controls/public/controls_callout/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +export const LazyControlsCallout = React.lazy(() => import('./controls_callout')); +export type { CalloutProps } from './controls_callout'; diff --git a/src/plugins/controls/public/index.ts b/src/plugins/controls/public/index.ts index c8118fcdb1d77..7caf9d19e49b2 100644 --- a/src/plugins/controls/public/index.ts +++ b/src/plugins/controls/public/index.ts @@ -39,6 +39,8 @@ export { type OptionsListEmbeddableInput, } from './control_types'; +export { LazyControlsCallout, type CalloutProps } from './controls_callout'; + export function plugin() { return new ControlsPlugin(); } diff --git a/src/plugins/dashboard/public/application/embeddable/viewport/_dashboard_viewport.scss b/src/plugins/dashboard/public/application/embeddable/viewport/_dashboard_viewport.scss index 5dad462803b3a..f9fc2c0a21633 100644 --- a/src/plugins/dashboard/public/application/embeddable/viewport/_dashboard_viewport.scss +++ b/src/plugins/dashboard/public/application/embeddable/viewport/_dashboard_viewport.scss @@ -6,8 +6,8 @@ width: 100%; } -.dshDashboardViewport-controlGroup { - margin: 0 $euiSizeS 0 $euiSizeS; +.dshDashboardViewport-controls { + margin: 0 $euiSizeS 0 $euiSizeS; padding-bottom: $euiSizeXS; } diff --git a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx index 43cc00f928c6c..0e700e058eef4 100644 --- a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx @@ -13,7 +13,12 @@ import { DashboardContainer, DashboardReactContextValue } from '../dashboard_con import { DashboardGrid } from '../grid'; import { context } from '../../../services/kibana_react'; import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen'; -import { ControlGroupContainer } from '../../../../../controls/public'; +import { + CalloutProps, + ControlGroupContainer, + LazyControlsCallout, +} from '../../../../../controls/public'; +import { withSuspense } from '../../../services/presentation_util'; export interface DashboardViewportProps { container: DashboardContainer; @@ -31,6 +36,8 @@ interface State { isEmbeddedExternally?: boolean; } +const ControlsCallout = withSuspense(LazyControlsCallout); + export class DashboardViewport extends React.Component { static contextType = context; public declare readonly context: DashboardReactContextValue; @@ -94,14 +101,24 @@ export class DashboardViewport extends React.Component {controlsEnabled ? ( -
+ <> + {isEditMode && panelCount !== 0 && controlGroup?.getPanelCount() === 0 ? ( + { + return controlGroup?.getCreateControlButton('callout'); + }} + /> + ) : null} +
+ ) : null}
), quickButtonGroup: , - addFromLibraryButton: ( - - ), extraButtons: [ , + , + dashboardAppState.dashboardContainer.controlGroup?.getToolbarButtons(), ], }} diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx b/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx index fa678dbdaae89..141a5c16d7d95 100644 --- a/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx +++ b/src/plugins/presentation_util/public/components/solution_toolbar/solution_toolbar.tsx @@ -10,7 +10,6 @@ import React, { ReactElement } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { - AddFromLibraryButton, QuickButtonGroup, PrimaryActionButton, SolutionToolbarButton, @@ -23,8 +22,9 @@ import './solution_toolbar.scss'; interface NamedSlots { primaryActionButton: ReactElement; quickButtonGroup?: ReactElement; - addFromLibraryButton?: ReactElement; - extraButtons?: Array>; + extraButtons?: Array< + ReactElement | undefined + >; } export interface Props { @@ -33,12 +33,7 @@ export interface Props { } export const SolutionToolbar = ({ isDarkModeEnabled, children }: Props) => { - const { - primaryActionButton, - quickButtonGroup, - addFromLibraryButton, - extraButtons = [], - } = children; + const { primaryActionButton, quickButtonGroup, extraButtons = [] } = children; const extra = extraButtons.map((button, index) => button ? ( @@ -61,9 +56,6 @@ export const SolutionToolbar = ({ isDarkModeEnabled, children }: Props) => { {quickButtonGroup ? {quickButtonGroup} : null} {extra} - {addFromLibraryButton ? ( - {addFromLibraryButton} - ) : null} diff --git a/test/functional/apps/dashboard/dashboard_controls_integration.ts b/test/functional/apps/dashboard/dashboard_controls_integration.ts index 96a0bcdc0346b..6f5c3722c10cb 100644 --- a/test/functional/apps/dashboard/dashboard_controls_integration.ts +++ b/test/functional/apps/dashboard/dashboard_controls_integration.ts @@ -51,12 +51,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.savedObjects.cleanStandardList(); }); - it('shows the empty control callout on a new dashboard', async () => { - await testSubjects.existOrFail('controls-empty'); + describe('Controls callout visibility', async () => { + describe('does not show the empty control callout on an empty dashboard', async () => { + it('in view mode', async () => { + await dashboard.saveDashboard('Test Controls Callout'); + await dashboard.clickCancelOutOfEditMode(); + await testSubjects.missingOrFail('controls-empty'); + }); + + it('in edit mode', async () => { + await dashboard.switchToEditMode(); + await testSubjects.missingOrFail('controls-empty'); + }); + }); + + it('show the empty control callout on a dashboard with panels', async () => { + await dashboardAddPanel.addVisualization('Rendering-Test:-animal-sounds-pie'); + await testSubjects.existOrFail('controls-empty'); + }); + + it('adding control hides the empty control callout', async () => { + await dashboardControls.createOptionsListControl({ + dataViewTitle: 'animals-*', + fieldName: 'sound.keyword', + }); + await testSubjects.missingOrFail('controls-empty'); + }); + + after(async () => { + await dashboard.clickCancelOutOfEditMode(); + await dashboard.gotoDashboardLandingPage(); + }); }); describe('Options List Control creation and editing experience', async () => { it('can add a new options list control from a blank state', async () => { + await dashboard.clickNewDashboard(); + await timePicker.setDefaultDataRange(); await dashboardControls.createOptionsListControl({ fieldName: 'machine.os.raw' }); expect(await dashboardControls.getControlsCount()).to.be(1); }); @@ -115,7 +146,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { let controlId: string; before(async () => { await dashboardAddPanel.addVisualization('Rendering-Test:-animal-sounds-pie'); - await dashboardControls.createOptionsListControl({ dataViewTitle: 'animals-*', fieldName: 'sound.keyword', diff --git a/test/functional/page_objects/dashboard_page_controls.ts b/test/functional/page_objects/dashboard_page_controls.ts index 1adc60b3596b6..5cf7cbe0b92e7 100644 --- a/test/functional/page_objects/dashboard_page_controls.ts +++ b/test/functional/page_objects/dashboard_page_controls.ts @@ -64,10 +64,8 @@ export class DashboardPageControls extends FtrService { public async openCreateControlFlyout(type: string) { this.log.debug(`Opening flyout for ${type} control`); - await this.testSubjects.click('controls-create-button'); - if (await this.testSubjects.exists('control-type-picker')) { - await this.testSubjects.click(`create-${type}-control`); - } + await this.testSubjects.click('dashboardControlsMenuButton'); + await this.testSubjects.click(`create-${type}-control`); await this.retry.try(async () => { await this.testSubjects.existOrFail('control-editor-flyout'); }); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx index db0b8a680e867..7ba216358596d 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx @@ -169,13 +169,13 @@ export const WorkpadHeader: FC = ({ {{ primaryActionButton: , quickButtonGroup: , - addFromLibraryButton: ( + extraButtons: [ - ), - extraButtons: [], + />, + , + ], }} From 2da341ce157adc95f67f7c6246673851a60441be Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Wed, 9 Mar 2022 20:39:00 +0100 Subject: [PATCH 136/140] Unskip CTI tests (#127313) --- .../integration/detection_alerts/cti_enrichments.spec.ts | 2 +- .../integration/detection_rules/indicator_match_rule.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts index c5e015b6382c2..0e4dbc9a95f9c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts @@ -33,7 +33,7 @@ import { openJsonView, openThreatIndicatorDetails } from '../../tasks/alerts_det import { ALERTS_URL } from '../../urls/navigation'; import { addsFieldsToTimeline } from '../../tasks/rule_details'; -describe.skip('CTI Enrichment', () => { +describe('CTI Enrichment', () => { before(() => { cleanKibana(); esArchiverLoad('threat_indicator'); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts index 9978045835f48..13d939875b1ff 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts @@ -114,7 +114,7 @@ import { goBackToAllRulesTable, getDetails } from '../../tasks/rule_details'; import { ALERTS_URL, RULE_CREATION } from '../../urls/navigation'; const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"'; -describe.skip('indicator match', () => { +describe('indicator match', () => { describe('Detection rules, Indicator Match', () => { const expectedUrls = getNewThreatIndicatorRule().referenceUrls.join(''); const expectedFalsePositives = getNewThreatIndicatorRule().falsePositivesExamples.join(''); From 8782cedc3549ae2a737dbebea176c2d654bbb0c0 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Wed, 9 Mar 2022 20:46:21 +0100 Subject: [PATCH 137/140] [SharedUX] Migrate PageTemplate > NoDataPage > NoDataCard (#127025) * [SharedUX] Migrate PageTemplate > NoData > NoDataCard * Add deprecation warning * Fix directory structure * Fix i18n * Applying Caroline's suggestions * Fix types * Fix typescripting * Update src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.stories.tsx Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> --- .../no_data_page/no_data_card/index.ts | 1 + .../public/components/page_template/index.tsx | 9 + .../__snapshots__/no_data_card.test.tsx.snap | 155 ++++++++++++++++++ .../no_data_page/no_data_card/index.ts | 8 + .../no_data_card/no_data_card.stories.tsx | 45 +++++ .../no_data_card/no_data_card.test.tsx | 41 +++++ .../no_data_card/no_data_card.tsx | 48 ++++++ .../no_data_page/no_data_card/types.ts | 29 ++++ 8 files changed, 336 insertions(+) create mode 100644 src/plugins/shared_ux/public/components/page_template/index.tsx create mode 100644 src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap create mode 100644 src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/index.ts create mode 100644 src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.stories.tsx create mode 100644 src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.test.tsx create mode 100644 src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.tsx create mode 100644 src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/types.ts diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts index e05d4d9675ca9..7f88c1a76e79b 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts @@ -7,4 +7,5 @@ */ export * from './elastic_agent_card'; +/** @deprecated Use `NoDataCard` from `src/plugins/shared_ux/page_template`. */ export * from './no_data_card'; diff --git a/src/plugins/shared_ux/public/components/page_template/index.tsx b/src/plugins/shared_ux/public/components/page_template/index.tsx new file mode 100644 index 0000000000000..1f686183c2edb --- /dev/null +++ b/src/plugins/shared_ux/public/components/page_template/index.tsx @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { NoDataCard } from './no_data_page/no_data_card'; diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap new file mode 100644 index 0000000000000..fccbbe3a9e8ee --- /dev/null +++ b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap @@ -0,0 +1,155 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NoDataCard props button 1`] = ` +
+
+ + Card title + +
+

+ Description +

+
+
+ +
+`; + +exports[`NoDataCard props href 1`] = ` +
+
+ + + Card title + + +
+

+ Description +

+
+
+ +
+`; + +exports[`NoDataCard props recommended 1`] = ` +
+
+ + Card title + +
+

+ Description +

+
+
+ + + Recommended + + +
+`; + +exports[`NoDataCard renders 1`] = ` +
+
+ + Card title + +
+

+ Description +

+
+
+
+`; diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/index.ts b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/index.ts new file mode 100644 index 0000000000000..5f226aba691a8 --- /dev/null +++ b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export { NoDataCard } from './no_data_card'; diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.stories.tsx b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.stories.tsx new file mode 100644 index 0000000000000..6f496d50d7272 --- /dev/null +++ b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.stories.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { NoDataCard } from './no_data_card'; +import type { NoDataCardProps } from './types'; + +export default { + title: 'No Data Card', + description: 'A wrapper around EuiCard, to be used on NoData page', +}; + +type Params = Pick; + +export const PureComponent = (params: Params) => { + return ( +
+ +
+ ); +}; + +PureComponent.argTypes = { + recommended: { + control: 'boolean', + defaultValue: false, + }, + button: { + control: { + type: 'text', + }, + defaultValue: 'Button text', + }, + description: { + control: { + type: 'text', + }, + defaultValue: 'This is a description', + }, +}; diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.test.tsx b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.test.tsx new file mode 100644 index 0000000000000..a809ede2dc617 --- /dev/null +++ b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.test.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { render } from 'enzyme'; +import React from 'react'; +import { NoDataCard } from './no_data_card'; + +describe('NoDataCard', () => { + test('renders', () => { + const component = render(); + expect(component).toMatchSnapshot(); + }); + + describe('props', () => { + test('recommended', () => { + const component = render( + + ); + expect(component).toMatchSnapshot(); + }); + + test('button', () => { + const component = render( + + ); + expect(component).toMatchSnapshot(); + }); + + test('href', () => { + const component = render( + + ); + expect(component).toMatchSnapshot(); + }); + }); +}); diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.tsx b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.tsx new file mode 100644 index 0000000000000..9f545985065dc --- /dev/null +++ b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/no_data_card.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiButton, EuiCard } from '@elastic/eui'; +import type { NoDataCardProps } from './types'; + +const recommendedLabel = i18n.translate('sharedUX.pageTemplate.noDataPage.recommendedLabel', { + defaultMessage: 'Recommended', +}); + +const defaultDescription = i18n.translate('sharedUX.pageTemplate.noDataCard.description', { + defaultMessage: `Proceed without collecting data`, +}); + +export const NoDataCard: FunctionComponent = ({ + recommended, + title, + button, + description, + ...cardRest +}) => { + const footer = () => { + if (typeof button !== 'string') { + return button; + } + return {button || title}; + }; + const label = recommended ? recommendedLabel : undefined; + const cardDescription = description || defaultDescription; + + return ( + + ); +}; diff --git a/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/types.ts b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/types.ts new file mode 100644 index 0000000000000..e08d9fdeaaa33 --- /dev/null +++ b/src/plugins/shared_ux/public/components/page_template/no_data_page/no_data_card/types.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiCardProps } from '@elastic/eui'; +import { MouseEventHandler, ReactNode } from 'react'; + +export type NoDataCardProps = Partial> & { + /** + * Applies the `Recommended` beta badge and makes the button `fill` + */ + recommended?: boolean; + /** + * Provide just a string for the button's label, or a whole component + */ + button?: string | ReactNode; + /** + * Remapping `onClick` to any element + */ + onClick?: MouseEventHandler; + /** + * Description for the card. If not provided, the default will be used. + */ + description?: string; +}; From 6456d184c0690fa6b3129a4cd142a050bbc38b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Wed, 9 Mar 2022 14:52:52 -0500 Subject: [PATCH 138/140] Cancel search queries on timeout regardless of rule type (#124374) * Initial commit * Add unit test * Change code for wrapping * Fix failing tests * Fix creation of wrappedClient * Fix merge issues * Remove TContext * Attempt at fixing meta stuff * Use esClient.child * Fix wrap scoped cluster client tests * Fix task cancel jest tests * Fix wrapEsClient to mutate the wrappedClient within the function * Update with main * Fix jest test failures * Add missing jest test from create_abortable_es_client_factory.test.ts * Fix merge Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/alerting/README.md | 3 +- x-pack/plugins/alerting/server/index.ts | 5 - ...create_abortable_es_client_factory.test.ts | 107 ------------ .../lib/create_abortable_es_client_factory.ts | 77 --------- .../lib/wrap_scoped_cluster_client.test.ts | 44 ++++- .../server/lib/wrap_scoped_cluster_client.ts | 5 + .../server/task_runner/task_runner.ts | 6 +- x-pack/plugins/alerting/server/types.ts | 2 - .../server/utils/rule_executor_test_utils.ts | 1 - .../routes/rules/preview_rules_route.ts | 10 +- .../utils/wrap_scoped_cluster_client.test.ts | 154 ++++++++++++++++++ .../rules/utils/wrap_scoped_cluster_client.ts | 111 +++++++++++++ .../rule_types/__mocks__/rule_type.ts | 1 - .../query/create_query_alert_type.test.ts | 4 +- .../signals/executors/threshold.test.ts | 2 +- .../signals/search_after_bulk_create.test.ts | 96 +++++------ .../signals/single_search_after.test.ts | 10 +- .../signals/single_search_after.ts | 5 +- .../alert_types/es_query/alert_type.test.ts | 16 +- .../server/alert_types/es_query/alert_type.ts | 6 +- .../alert_types/index_threshold/alert_type.ts | 6 +- .../server/data/lib/time_series_query.test.ts | 6 +- .../server/data/lib/time_series_query.ts | 9 +- .../server/data/routes/time_series_query.ts | 7 +- .../plugins/alerts/server/alert_types.ts | 2 +- 25 files changed, 395 insertions(+), 300 deletions(-) delete mode 100644 x-pack/plugins/alerting/server/lib/create_abortable_es_client_factory.test.ts delete mode 100644 x-pack/plugins/alerting/server/lib/create_abortable_es_client_factory.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/wrap_scoped_cluster_client.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/wrap_scoped_cluster_client.ts diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index a18c62047a0de..903d190a216bd 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -116,7 +116,6 @@ This is the primary function for a rule type. Whenever the rule needs to execute |services.log(tags, [data], [timestamp])|Use this to create server logs. (This is the same function as server.log)| |services.shouldWriteAlerts()|This returns a boolean indicating whether the executor should write out alerts as data. This is determined by whether rule execution has been cancelled due to timeout AND whether both the Kibana `cancelAlertsOnRuleTimeout` flag and the rule type `cancelAlertsOnRuleTimeout` are set to `true`.| |services.shouldStopExecution()|This returns a boolean indicating whether rule execution has been cancelled due to timeout.| -|services.search|This provides an implementation of Elasticsearch client `search` function that aborts searches if rule execution is cancelled mid-search.| |startedAt|The date and time the rule type started execution.| |previousStartedAt|The previous date and time the rule type started a successful execution.| |params|Parameters for the execution. This is where the parameters you require will be passed in. (e.g. threshold). Use rule type validation to ensure values are set before execution.| @@ -321,7 +320,7 @@ const myRuleType: RuleType< // Query Elasticsearch using a cancellable search // If rule execution is cancelled mid-search, the search request will be aborted // and an error will be thrown. - const esClient = services.search.asCurrentUser; + const esClient = services.scopedClusterClient.asCurrentUser; await esClient.search(esQuery); // Call a function to get the server's current CPU usage diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index 46f0b7e284164..343a4e1039918 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -36,11 +36,6 @@ export type { PublicAlert as Alert } from './alert'; export { parseDuration } from './lib'; export { getEsErrorMessage } from './lib/errors'; export type { PublicAlertingConfig } from './config'; -export type { - IAbortableEsClient, - IAbortableClusterClient, -} from './lib/create_abortable_es_client_factory'; -export { createAbortableEsClientFactory } from './lib/create_abortable_es_client_factory'; export { ReadOperations, AlertingAuthorizationFilterType, diff --git a/x-pack/plugins/alerting/server/lib/create_abortable_es_client_factory.test.ts b/x-pack/plugins/alerting/server/lib/create_abortable_es_client_factory.test.ts deleted file mode 100644 index d6ac850e59214..0000000000000 --- a/x-pack/plugins/alerting/server/lib/create_abortable_es_client_factory.test.ts +++ /dev/null @@ -1,107 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; -import { createAbortableEsClientFactory } from './create_abortable_es_client_factory'; - -const esQuery = { - body: { query: { bool: { filter: { range: { '@timestamp': { gte: 0 } } } } } }, -}; - -describe('createAbortableEsClientFactory', () => { - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - test('searches with asInternalUser when specified', async () => { - const abortController = new AbortController(); - const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); - const abortableSearchClient = createAbortableEsClientFactory({ - scopedClusterClient, - abortController, - }); - - await abortableSearchClient.asInternalUser.search(esQuery); - expect(scopedClusterClient.asInternalUser.search).toHaveBeenCalledWith(esQuery, { - signal: abortController.signal, - meta: true, - }); - expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); - }); - - test('searches with asCurrentUser when specified', async () => { - const abortController = new AbortController(); - const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); - const abortableSearchClient = createAbortableEsClientFactory({ - scopedClusterClient, - abortController, - }); - - await abortableSearchClient.asCurrentUser.search(esQuery); - expect(scopedClusterClient.asCurrentUser.search).toHaveBeenCalledWith(esQuery, { - signal: abortController.signal, - meta: true, - }); - expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); - }); - - test('uses search options when specified', async () => { - const abortController = new AbortController(); - const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); - const abortableSearchClient = createAbortableEsClientFactory({ - scopedClusterClient, - abortController, - }); - - await abortableSearchClient.asInternalUser.search(esQuery, { ignore: [404] }); - expect(scopedClusterClient.asInternalUser.search).toHaveBeenCalledWith(esQuery, { - ignore: [404], - signal: abortController.signal, - meta: true, - }); - expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); - }); - - test('re-throws error when search throws error', async () => { - const abortController = new AbortController(); - const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); - scopedClusterClient.asInternalUser.search.mockRejectedValueOnce( - new Error('something went wrong!') - ); - const abortableSearchClient = createAbortableEsClientFactory({ - scopedClusterClient, - abortController, - }); - - await expect( - abortableSearchClient.asInternalUser.search - ).rejects.toThrowErrorMatchingInlineSnapshot(`"something went wrong!"`); - }); - - test('throws error when search throws abort error', async () => { - const abortController = new AbortController(); - abortController.abort(); - const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); - scopedClusterClient.asInternalUser.search.mockRejectedValueOnce( - new Error('Request has been aborted by the user') - ); - const abortableSearchClient = createAbortableEsClientFactory({ - scopedClusterClient, - abortController, - }); - - await expect( - abortableSearchClient.asInternalUser.search - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Search has been aborted due to cancelled execution"` - ); - }); -}); diff --git a/x-pack/plugins/alerting/server/lib/create_abortable_es_client_factory.ts b/x-pack/plugins/alerting/server/lib/create_abortable_es_client_factory.ts deleted file mode 100644 index a24ad544557d6..0000000000000 --- a/x-pack/plugins/alerting/server/lib/create_abortable_es_client_factory.ts +++ /dev/null @@ -1,77 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TransportRequestOptions, TransportResult } from '@elastic/elasticsearch'; -import type { SearchRequest, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; -import type { - SearchRequest as SearchRequestWithBody, - AggregationsAggregate, -} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { IScopedClusterClient } from 'src/core/server'; - -export interface IAbortableEsClient { - search: >( - query: SearchRequest | SearchRequestWithBody, - options?: TransportRequestOptions - ) => Promise, unknown>>; -} - -export interface IAbortableClusterClient { - readonly asInternalUser: IAbortableEsClient; - readonly asCurrentUser: IAbortableEsClient; -} - -export interface CreateAbortableEsClientFactoryOpts { - scopedClusterClient: IScopedClusterClient; - abortController: AbortController; -} - -export function createAbortableEsClientFactory(opts: CreateAbortableEsClientFactoryOpts) { - const { scopedClusterClient, abortController } = opts; - return { - asInternalUser: { - search: async >( - query: SearchRequest | SearchRequestWithBody, - options?: TransportRequestOptions - ) => { - try { - const searchOptions = options ?? {}; - return await scopedClusterClient.asInternalUser.search(query, { - ...searchOptions, - signal: abortController.signal, - meta: true, - }); - } catch (e) { - if (abortController.signal.aborted) { - throw new Error('Search has been aborted due to cancelled execution'); - } - throw e; - } - }, - }, - asCurrentUser: { - search: async >( - query: SearchRequest | SearchRequestWithBody, - options?: TransportRequestOptions - ) => { - try { - const searchOptions = options ?? {}; - return await scopedClusterClient.asCurrentUser.search(query, { - ...searchOptions, - signal: abortController.signal, - meta: true, - }); - } catch (e) { - if (abortController.signal.aborted) { - throw new Error('Search has been aborted due to cancelled execution'); - } - throw e; - } - }, - }, - }; -} diff --git a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts index d6101c9c6cec4..046b18553d042 100644 --- a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts +++ b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.test.ts @@ -37,6 +37,7 @@ describe('wrapScopedClusterClient', () => { }); test('searches with asInternalUser when specified', async () => { + const abortController = new AbortController(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); @@ -47,15 +48,19 @@ describe('wrapScopedClusterClient', () => { scopedClusterClient, rule, logger, + abortController, }).client(); await wrappedSearchClient.asInternalUser.search(esQuery); - expect(asInternalUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, {}); + expect(asInternalUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, { + signal: abortController.signal, + }); expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); }); test('searches with asCurrentUser when specified', async () => { + const abortController = new AbortController(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); @@ -66,15 +71,19 @@ describe('wrapScopedClusterClient', () => { scopedClusterClient, rule, logger, + abortController, }).client(); await wrappedSearchClient.asCurrentUser.search(esQuery); - expect(asCurrentUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, {}); + expect(asCurrentUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, { + signal: abortController.signal, + }); expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); }); test('uses search options when specified', async () => { + const abortController = new AbortController(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); @@ -85,17 +94,20 @@ describe('wrapScopedClusterClient', () => { scopedClusterClient, rule, logger, + abortController, }).client(); await wrappedSearchClient.asInternalUser.search(esQuery, { ignore: [404] }); expect(asInternalUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, { ignore: [404], + signal: abortController.signal, }); expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); }); test('re-throws error when search throws error', async () => { + const abortController = new AbortController(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); @@ -107,6 +119,7 @@ describe('wrapScopedClusterClient', () => { scopedClusterClient, rule, logger, + abortController, }).client(); await expect( @@ -115,6 +128,7 @@ describe('wrapScopedClusterClient', () => { }); test('handles empty search result object', async () => { + const abortController = new AbortController(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); @@ -127,6 +141,7 @@ describe('wrapScopedClusterClient', () => { scopedClusterClient, rule, logger, + abortController, }); const wrappedSearchClient = wrappedSearchClientFactory.client(); @@ -142,6 +157,7 @@ describe('wrapScopedClusterClient', () => { }); test('keeps track of number of queries', async () => { + const abortController = new AbortController(); const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); const childClient = elasticsearchServiceMock.createElasticsearchClient(); @@ -154,6 +170,7 @@ describe('wrapScopedClusterClient', () => { scopedClusterClient, rule, logger, + abortController, }); const wrappedSearchClient = wrappedSearchClientFactory.client(); await wrappedSearchClient.asInternalUser.search(esQuery); @@ -172,4 +189,27 @@ describe('wrapScopedClusterClient', () => { `executing query for rule .test-rule-type:abcdefg in space my-space - {\"body\":{\"query\":{\"bool\":{\"filter\":{\"range\":{\"@timestamp\":{\"gte\":0}}}}}}} - with options {}` ); }); + + test('throws error when search throws abort error', async () => { + const abortController = new AbortController(); + abortController.abort(); + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); + childClient.search.mockRejectedValueOnce(new Error('Request has been aborted by the user')); + + const abortableSearchClient = createWrappedScopedClusterClientFactory({ + scopedClusterClient, + rule, + logger, + abortController, + }).client(); + + await expect( + abortableSearchClient.asInternalUser.search + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Search has been aborted due to cancelled execution"` + ); + }); }); diff --git a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts index 2b71f95cd9f1c..69d4817f21a4e 100644 --- a/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts +++ b/x-pack/plugins/alerting/server/lib/wrap_scoped_cluster_client.ts @@ -29,6 +29,7 @@ interface WrapScopedClusterClientFactoryOpts { scopedClusterClient: IScopedClusterClient; rule: RuleInfo; logger: Logger; + abortController: AbortController; } type WrapScopedClusterClientOpts = WrapScopedClusterClientFactoryOpts & { @@ -140,6 +141,7 @@ function getWrappedSearchFn(opts: WrapEsClientOpts) { ); const result = (await originalSearch.call(opts.esClient, params, { ...searchOptions, + signal: opts.abortController.signal, })) as | TransportResult, unknown> | SearchResponse; @@ -160,6 +162,9 @@ function getWrappedSearchFn(opts: WrapEsClientOpts) { opts.logMetricsFn({ esSearchDuration: took ?? 0, totalSearchDuration: durationMs }); return result; } catch (e) { + if (opts.abortController.signal.aborted) { + throw new Error('Search has been aborted due to cancelled execution'); + } throw e; } } diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index ccf9659a8e67d..b4cb262e571ee 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -62,7 +62,6 @@ import { createAlertEventLogRecordObject, Event, } from '../lib/create_alert_event_log_record_object'; -import { createAbortableEsClientFactory } from '../lib/create_abortable_es_client_factory'; import { createWrappedScopedClusterClientFactory } from '../lib'; import { getRecoveredAlerts } from '../lib'; import { @@ -339,6 +338,7 @@ export class TaskRunner< spaceId, }, logger: this.logger, + abortController: this.searchAbortController, }); let updatedRuleTypeState: void | Record; @@ -375,10 +375,6 @@ export class TaskRunner< }), shouldWriteAlerts: () => this.shouldLogAndScheduleActionsForAlerts(), shouldStopExecution: () => this.cancelled, - search: createAbortableEsClientFactory({ - scopedClusterClient, - abortController: this.searchAbortController, - }), }, params, state: alertTypeState as State, diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 5499ba0c76caf..ea7e12b320d18 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -41,7 +41,6 @@ import { MappedParams, } from '../common'; import { LicenseType } from '../../licensing/server'; -import { IAbortableClusterClient } from './lib/create_abortable_es_client_factory'; export type WithoutQueryAndParams = Pick>; export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined; @@ -82,7 +81,6 @@ export interface AlertServices< }; shouldWriteAlerts: () => boolean; shouldStopExecution: () => boolean; - search: IAbortableClusterClient; } export interface AlertExecutorOptions< diff --git a/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts b/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts index 38843d95d6b73..53b688a87845d 100644 --- a/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts +++ b/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts @@ -74,7 +74,6 @@ export const createDefaultAlertExecutorOptions = < scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), shouldWriteAlerts: () => shouldWriteAlerts, shouldStopExecution: () => false, - search: alertsMock.createAlertServices().search, }, state, updatedBy: null, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts index 8b063e4f1b65c..3deb87e864f25 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -20,6 +20,7 @@ import { SetupPlugins } from '../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { createRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/create_rules_type_dependents'; import { DETECTION_ENGINE_RULES_PREVIEW } from '../../../../../common/constants'; +import { wrapScopedClusterClient } from './utils/wrap_scoped_cluster_client'; import { previewRulesSchema, RulePreviewLogs, @@ -34,7 +35,7 @@ import { } from '../../../../../../alerting/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ExecutorType } from '../../../../../../alerting/server/types'; -import { Alert, createAbortableEsClientFactory } from '../../../../../../alerting/server'; +import { Alert } from '../../../../../../alerting/server'; import { ConfigType } from '../../../../config'; import { alertInstanceFactoryStub } from '../../signals/preview/alert_instance_factory_stub'; import { CreateRuleOptions, CreateSecurityRuleTypeWrapperProps } from '../../rule_types/types'; @@ -197,12 +198,11 @@ export const previewRulesRoute = async ( shouldWriteAlerts, shouldStopExecution: () => false, alertFactory, - search: createAbortableEsClientFactory({ - scopedClusterClient: context.core.elasticsearch.client, + savedObjectsClient: context.core.savedObjects.client, + scopedClusterClient: wrapScopedClusterClient({ abortController, + scopedClusterClient: context.core.elasticsearch.client, }), - savedObjectsClient: context.core.savedObjects.client, - scopedClusterClient: context.core.elasticsearch.client, uiSettingsClient: context.core.uiSettings.client, }, spaceId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/wrap_scoped_cluster_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/wrap_scoped_cluster_client.test.ts new file mode 100644 index 0000000000000..ce037566e8284 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/wrap_scoped_cluster_client.test.ts @@ -0,0 +1,154 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Client } from '@elastic/elasticsearch'; +import { elasticsearchServiceMock } from 'src/core/server/mocks'; +import { wrapScopedClusterClient } from './wrap_scoped_cluster_client'; + +const esQuery = { + body: { query: { bool: { filter: { range: { '@timestamp': { gte: 0 } } } } } }, +}; + +describe('wrapScopedClusterClient', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + test('searches with asInternalUser when specified', async () => { + const abortController = new AbortController(); + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); + const asInternalUserWrappedSearchFn = childClient.search; + + const wrappedSearchClient = wrapScopedClusterClient({ + scopedClusterClient, + abortController, + }); + await wrappedSearchClient.asInternalUser.search(esQuery); + + expect(asInternalUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, { + signal: abortController.signal, + }); + expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); + expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); + }); + + test('searches with asCurrentUser when specified', async () => { + const abortController = new AbortController(); + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + scopedClusterClient.asCurrentUser.child.mockReturnValue(childClient as unknown as Client); + const asCurrentUserWrappedSearchFn = childClient.search; + + const wrappedSearchClient = wrapScopedClusterClient({ + scopedClusterClient, + abortController, + }); + await wrappedSearchClient.asCurrentUser.search(esQuery); + + expect(asCurrentUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, { + signal: abortController.signal, + }); + expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); + expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); + }); + + test('uses search options when specified', async () => { + const abortController = new AbortController(); + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); + const asInternalUserWrappedSearchFn = childClient.search; + + const wrappedSearchClient = wrapScopedClusterClient({ + scopedClusterClient, + abortController, + }); + await wrappedSearchClient.asInternalUser.search(esQuery, { ignore: [404] }); + + expect(asInternalUserWrappedSearchFn).toHaveBeenCalledWith(esQuery, { + ignore: [404], + signal: abortController.signal, + }); + expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); + expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); + }); + + test('re-throws error when search throws error', async () => { + const abortController = new AbortController(); + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); + const asInternalUserWrappedSearchFn = childClient.search; + + asInternalUserWrappedSearchFn.mockRejectedValueOnce(new Error('something went wrong!')); + const wrappedSearchClient = wrapScopedClusterClient({ + scopedClusterClient, + abortController, + }); + + await expect( + wrappedSearchClient.asInternalUser.search + ).rejects.toThrowErrorMatchingInlineSnapshot(`"something went wrong!"`); + }); + + test('handles empty search result object', async () => { + const abortController = new AbortController(); + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); + const asInternalUserWrappedSearchFn = childClient.search; + // @ts-ignore incomplete return type + asInternalUserWrappedSearchFn.mockResolvedValue({}); + + const wrappedSearchClient = wrapScopedClusterClient({ + scopedClusterClient, + abortController, + }); + + await wrappedSearchClient.asInternalUser.search(esQuery); + + expect(asInternalUserWrappedSearchFn).toHaveBeenCalledTimes(1); + expect(scopedClusterClient.asInternalUser.search).not.toHaveBeenCalled(); + expect(scopedClusterClient.asCurrentUser.search).not.toHaveBeenCalled(); + }); + + test('throws error when search throws abort error', async () => { + const abortController = new AbortController(); + abortController.abort(); + const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + const childClient = elasticsearchServiceMock.createElasticsearchClient(); + + scopedClusterClient.asInternalUser.child.mockReturnValue(childClient as unknown as Client); + childClient.search.mockRejectedValueOnce(new Error('Request has been aborted by the user')); + + const abortableSearchClient = wrapScopedClusterClient({ + scopedClusterClient, + abortController, + }); + + await expect( + abortableSearchClient.asInternalUser.search + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Search has been aborted due to cancelled execution"` + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/wrap_scoped_cluster_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/wrap_scoped_cluster_client.ts new file mode 100644 index 0000000000000..f007a7dc745cd --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/wrap_scoped_cluster_client.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + TransportRequestOptions, + TransportResult, + TransportRequestOptionsWithMeta, + TransportRequestOptionsWithOutMeta, +} from '@elastic/elasticsearch'; +import type { + SearchRequest, + SearchResponse, + AggregateName, +} from '@elastic/elasticsearch/lib/api/types'; +import type { + SearchRequest as SearchRequestWithBody, + AggregationsAggregate, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IScopedClusterClient, ElasticsearchClient } from 'src/core/server'; + +interface WrapScopedClusterClientOpts { + scopedClusterClient: IScopedClusterClient; + abortController: AbortController; +} + +type WrapEsClientOpts = Omit & { + esClient: ElasticsearchClient; +}; + +export function wrapScopedClusterClient(opts: WrapScopedClusterClientOpts): IScopedClusterClient { + const { scopedClusterClient, ...rest } = opts; + return { + asInternalUser: wrapEsClient({ + ...rest, + esClient: scopedClusterClient.asInternalUser, + }), + asCurrentUser: wrapEsClient({ + ...rest, + esClient: scopedClusterClient.asCurrentUser, + }), + }; +} + +function wrapEsClient(opts: WrapEsClientOpts): ElasticsearchClient { + const { esClient, ...rest } = opts; + + const wrappedClient = esClient.child({}); + + // Mutating the functions we want to wrap + wrappedClient.search = getWrappedSearchFn({ esClient: wrappedClient, ...rest }); + + return wrappedClient; +} + +function getWrappedSearchFn(opts: WrapEsClientOpts) { + const originalSearch = opts.esClient.search; + + // A bunch of overloads to make TypeScript happy + async function search< + TDocument = unknown, + TAggregations = Record + >( + params?: SearchRequest | SearchRequestWithBody, + options?: TransportRequestOptionsWithOutMeta + ): Promise>; + async function search< + TDocument = unknown, + TAggregations = Record + >( + params?: SearchRequest | SearchRequestWithBody, + options?: TransportRequestOptionsWithMeta + ): Promise, unknown>>; + async function search< + TDocument = unknown, + TAggregations = Record + >( + params?: SearchRequest | SearchRequestWithBody, + options?: TransportRequestOptions + ): Promise>; + async function search< + TDocument = unknown, + TAggregations = Record + >( + params?: SearchRequest | SearchRequestWithBody, + options?: TransportRequestOptions + ): Promise< + | TransportResult, unknown> + | SearchResponse + > { + try { + const searchOptions = options ?? {}; + return (await originalSearch.call(opts.esClient, params, { + ...searchOptions, + signal: opts.abortController.signal, + })) as + | TransportResult, unknown> + | SearchResponse; + } catch (e) { + if (opts.abortController.signal.aborted) { + throw new Error('Search has been aborted due to cancelled execution'); + } + throw e; + } + } + + return search; +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index 3d390cac6b91f..2129596a1b204 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -73,7 +73,6 @@ export const createRuleTypeMocks = ( } as SavedObject); const services = { - search: elasticsearchServiceMock.createScopedClusterClient(), savedObjectsClient: mockSavedObjectsClient, scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), alertFactory: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts index 7d0cfa55922b0..d9119b3cff8dd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts @@ -60,7 +60,7 @@ describe('Custom Query Alerts', () => { alerting.registerType(queryAlertType); - services.search.asCurrentUser.search.mockReturnValue( + services.scopedClusterClient.asCurrentUser.search.mockReturnValue( elasticsearchClientMock.createSuccessTransportRequestPromise({ hits: { hits: [], @@ -104,7 +104,7 @@ describe('Custom Query Alerts', () => { alerting.registerType(queryAlertType); - services.search.asCurrentUser.search.mockReturnValue( + services.scopedClusterClient.asCurrentUser.search.mockReturnValue( elasticsearchClientMock.createSuccessTransportRequestPromise({ hits: { hits: [sampleDocNoSortId()], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts index 971be4653e153..e01e3498c2c7a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts @@ -41,7 +41,7 @@ describe('threshold_executor', () => { beforeEach(() => { alertServices = alertsMock.createAlertServices(); - alertServices.search.asCurrentUser.search.mockResolvedValue( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(sampleEmptyDocSearchResults()) ); logger = loggingSystemMock.createLogger(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index b44c4cbe1661f..7d22d58efdd6f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -80,7 +80,7 @@ describe('searchAfterAndBulkCreate', () => { }); test('should return success with number of searches less than max signals', async () => { - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3)) ) @@ -99,7 +99,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(3, 6)) ) @@ -118,7 +118,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(6, 9)) ) @@ -137,7 +137,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(9, 12)) ) @@ -156,7 +156,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( sampleDocSearchResultsNoSortIdNoHits() ) @@ -194,13 +194,13 @@ describe('searchAfterAndBulkCreate', () => { wrapHits, }); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(5); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(5); expect(createdSignalsCount).toEqual(4); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('should return success with number of searches less than max signals with gap', async () => { - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3)) ) @@ -218,7 +218,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(3, 6)) ) @@ -237,7 +237,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(6, 9)) ) @@ -256,7 +256,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( sampleDocSearchResultsNoSortIdNoHits() ) @@ -293,13 +293,13 @@ describe('searchAfterAndBulkCreate', () => { wrapHits, }); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(4); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(4); expect(createdSignalsCount).toEqual(3); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('should return success when no search results are in the allowlist', async () => { - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3)) ) @@ -336,7 +336,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( sampleDocSearchResultsNoSortIdNoHits() ) @@ -373,7 +373,7 @@ describe('searchAfterAndBulkCreate', () => { wrapHits, }); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(2); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(2); expect(createdSignalsCount).toEqual(4); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); @@ -385,7 +385,7 @@ describe('searchAfterAndBulkCreate', () => { { ...getSearchListItemResponseMock(), value: ['3.3.3.3'] }, ]; listClient.searchListItemByValues = jest.fn().mockResolvedValue(searchListItems); - mockService.search.asCurrentUser.search + mockService.scopedClusterClient.asCurrentUser.search .mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3), [ @@ -433,7 +433,7 @@ describe('searchAfterAndBulkCreate', () => { wrapHits, }); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(2); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(2); expect(createdSignalsCount).toEqual(0); // should not create any signals because all events were in the allowlist expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); @@ -473,7 +473,7 @@ describe('searchAfterAndBulkCreate', () => { }, ], }); - mockService.search.asCurrentUser.search + mockService.scopedClusterClient.asCurrentUser.search .mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId( @@ -511,7 +511,7 @@ describe('searchAfterAndBulkCreate', () => { wrapHits, }); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(2); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(2); expect(createdSignalsCount).toEqual(4); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); @@ -525,7 +525,7 @@ describe('searchAfterAndBulkCreate', () => { ]; listClient.searchListItemByValues = jest.fn().mockResolvedValue(searchListItems); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithNoSortId(4, 4, someGuids.slice(0, 3), [ '1.1.1.1', @@ -567,13 +567,13 @@ describe('searchAfterAndBulkCreate', () => { wrapHits, }); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(1); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(1); expect(createdSignalsCount).toEqual(0); // should not create any signals because all events were in the allowlist expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('should return success when no sortId present but search results are in the allowlist', async () => { - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithNoSortId(4, 4, someGuids.slice(0, 3)) ) @@ -641,13 +641,13 @@ describe('searchAfterAndBulkCreate', () => { wrapHits, }); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(1); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(1); expect(createdSignalsCount).toEqual(4); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('should return success when no exceptions list provided', async () => { - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 4, someGuids.slice(0, 3)) ) @@ -684,7 +684,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( sampleDocSearchResultsNoSortIdNoHits() ) @@ -717,7 +717,7 @@ describe('searchAfterAndBulkCreate', () => { wrapHits, }); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(2); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(2); expect(createdSignalsCount).toEqual(4); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); @@ -735,7 +735,7 @@ describe('searchAfterAndBulkCreate', () => { }, }, ]; - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3)) ) @@ -787,7 +787,7 @@ describe('searchAfterAndBulkCreate', () => { }, }, ]; - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(sampleEmptyDocSearchResults()) ); listClient.searchListItemByValues = jest.fn(({ value }) => @@ -822,23 +822,9 @@ describe('searchAfterAndBulkCreate', () => { }); test('if returns false when singleSearchAfter throws an exception', async () => { - mockService.search.asCurrentUser.search - .mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - took: 100, - errors: false, - items: [ - { - create: { - status: 201, - }, - }, - ], - }) - ) - .mockImplementation(() => { - throw Error('Fake Error'); // throws the exception we are testing - }); + mockService.scopedClusterClient.asCurrentUser.search.mockImplementation(() => { + throw Error('Fake Error'); // throws the exception we are testing + }); listClient.searchListItemByValues = jest.fn(({ value }) => Promise.resolve( value.slice(0, 2).map((item) => ({ @@ -903,7 +889,7 @@ describe('searchAfterAndBulkCreate', () => { }, ], }; - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3)) ) @@ -911,7 +897,7 @@ describe('searchAfterAndBulkCreate', () => { mockService.scopedClusterClient.asCurrentUser.bulk.mockResponseOnce(bulkItem); // adds the response with errors we are testing - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(3, 6)) ) @@ -930,7 +916,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(6, 9)) ) @@ -949,7 +935,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(9, 12)) ) @@ -968,7 +954,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( sampleDocSearchResultsNoSortIdNoHits() ) @@ -994,13 +980,13 @@ describe('searchAfterAndBulkCreate', () => { }); expect(success).toEqual(false); expect(errors).toEqual(['error on creation']); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(5); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(5); expect(createdSignalsCount).toEqual(4); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); it('invokes the enrichment callback with signal search results', async () => { - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3)) ) @@ -1019,7 +1005,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(3, 6)) ) @@ -1038,7 +1024,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( repeatedSearchResultsWithSortId(4, 1, someGuids.slice(6, 9)) ) @@ -1057,7 +1043,7 @@ describe('searchAfterAndBulkCreate', () => { ], }); - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( sampleDocSearchResultsNoSortIdNoHits() ) @@ -1097,7 +1083,7 @@ describe('searchAfterAndBulkCreate', () => { }) ); expect(success).toEqual(true); - expect(mockService.search.asCurrentUser.search).toHaveBeenCalledTimes(4); + expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(4); expect(createdSignalsCount).toEqual(3); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts index 7132ad06d0ca2..d00925af74316 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts @@ -30,7 +30,7 @@ describe('singleSearchAfter', () => { }); test('if singleSearchAfter works without a given sort id', async () => { - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(sampleDocSearchResultsNoSortId()) ); const { searchResult } = await singleSearchAfter({ @@ -48,7 +48,7 @@ describe('singleSearchAfter', () => { expect(searchResult).toEqual(sampleDocSearchResultsNoSortId()); }); test('if singleSearchAfter returns an empty failure array', async () => { - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(sampleDocSearchResultsNoSortId()) ); const { searchErrors } = await singleSearchAfter({ @@ -83,7 +83,7 @@ describe('singleSearchAfter', () => { }, }, ]; - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise({ took: 10, timed_out: false, @@ -119,7 +119,7 @@ describe('singleSearchAfter', () => { }); test('if singleSearchAfter works with a given sort id', async () => { const searchAfterSortIds = ['1234567891111']; - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( sampleDocSearchResultsWithSortId() ) @@ -140,7 +140,7 @@ describe('singleSearchAfter', () => { }); test('if singleSearchAfter throws error', async () => { const searchAfterSortIds = ['1234567891111']; - mockService.search.asCurrentUser.search.mockResolvedValueOnce( + mockService.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createErrorTransportRequestPromise(new Error('Fake Error')) ); await expect( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts index 3304e2507fe4f..62b0097c17e75 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts @@ -72,8 +72,9 @@ export const singleSearchAfter = async ({ const start = performance.now(); const { body: nextSearchAfterResult } = - await services.search.asCurrentUser.search( - searchAfterQuery as estypes.SearchRequest + await services.scopedClusterClient.asCurrentUser.search( + searchAfterQuery as estypes.SearchRequest, + { meta: true } ); const end = performance.now(); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts index 307496e2be391..e117f1db008f0 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts @@ -134,7 +134,7 @@ describe('alertType', () => { const alertServices: AlertServicesMock = alertsMock.createAlertServices(); const searchResult: ESSearchResponse = generateResults([]); - alertServices.search.asCurrentUser.search.mockResolvedValueOnce( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(searchResult) ); @@ -213,7 +213,7 @@ describe('alertType', () => { 'time-field': newestDocumentTimestamp - 2000, }, ]); - alertServices.search.asCurrentUser.search.mockResolvedValueOnce( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(searchResult) ); @@ -286,7 +286,7 @@ describe('alertType', () => { const previousTimestamp = Date.now(); const newestDocumentTimestamp = previousTimestamp + 1000; - alertServices.search.asCurrentUser.search.mockResolvedValueOnce( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( generateResults([ { @@ -356,7 +356,7 @@ describe('alertType', () => { const oldestDocumentTimestamp = Date.now(); - alertServices.search.asCurrentUser.search.mockResolvedValueOnce( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( generateResults([ { @@ -437,7 +437,7 @@ describe('alertType', () => { const oldestDocumentTimestamp = Date.now(); - alertServices.search.asCurrentUser.search.mockResolvedValueOnce( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( generateResults([ { @@ -500,7 +500,7 @@ describe('alertType', () => { }); const newestDocumentTimestamp = oldestDocumentTimestamp + 5000; - alertServices.search.asCurrentUser.search.mockResolvedValueOnce( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( generateResults([ { @@ -545,7 +545,7 @@ describe('alertType', () => { const oldestDocumentTimestamp = Date.now(); - alertServices.search.asCurrentUser.search.mockResolvedValueOnce( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( generateResults( [ @@ -628,7 +628,7 @@ describe('alertType', () => { const oldestDocumentTimestamp = Date.now(); - alertServices.search.asCurrentUser.search.mockResolvedValueOnce( + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( generateResults( [ diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts index 6a1fcc6a3d7bb..d0a23dd403419 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts @@ -160,10 +160,10 @@ export function getAlertType(logger: Logger): RuleType< > ) { const { alertId, name, services, params, state } = options; - const { alertFactory, search } = services; + const { alertFactory, scopedClusterClient } = services; const previousTimestamp = state.latestTimestamp; - const abortableEsClient = search.asCurrentUser; + const esClient = scopedClusterClient.asCurrentUser; const { parsedQuery, dateStart, dateEnd } = getSearchParams(params); const compareFn = ComparatorFns.get(params.thresholdComparator); @@ -224,7 +224,7 @@ export function getAlertType(logger: Logger): RuleType< logger.debug(`alert ${ES_QUERY_ID}:${alertId} "${name}" query - ${JSON.stringify(query)}`); - const { body: searchResult } = await abortableEsClient.search(query); + const { body: searchResult } = await esClient.search(query, { meta: true }); logger.debug( `alert ${ES_QUERY_ID}:${alertId} "${name}" result - ${JSON.stringify(searchResult)}` diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts index 7725721ed8efa..1d838fda0e8e0 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts @@ -135,7 +135,7 @@ export function getAlertType( options: AlertExecutorOptions ) { const { alertId: ruleId, name, services, params } = options; - const { alertFactory, search } = services; + const { alertFactory, scopedClusterClient } = services; const compareFn = ComparatorFns.get(params.thresholdComparator); if (compareFn == null) { @@ -149,7 +149,7 @@ export function getAlertType( ); } - const abortableEsClient = search.asCurrentUser; + const esClient = scopedClusterClient.asCurrentUser; const date = new Date().toISOString(); // the undefined values below are for config-schema optional types const queryParams: TimeSeriesQuery = { @@ -171,7 +171,7 @@ export function getAlertType( await data ).timeSeriesQuery({ logger, - abortableEsClient, + esClient, query: queryParams, }); logger.debug(`rule ${ID}:${ruleId} "${name}" query result: ${JSON.stringify(result)}`); diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts index c0528cf85bd5b..fb4879fa9ba39 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts @@ -29,16 +29,16 @@ const DefaultQueryParams: TimeSeriesQuery = { }; describe('timeSeriesQuery', () => { - const abortableEsClient = alertsMock.createAlertServices().search.asCurrentUser; + const esClient = alertsMock.createAlertServices().scopedClusterClient.asCurrentUser; const logger = loggingSystemMock.create().get() as jest.Mocked; const params = { logger, - abortableEsClient, + esClient, query: DefaultQueryParams, }; it('fails as expected when the callCluster call fails', async () => { - abortableEsClient.search = jest.fn().mockRejectedValue(new Error('woopsie')); + esClient.search.mockRejectedValue(new Error('woopsie')); await timeSeriesQuery(params); expect(logger.warn.mock.calls[0]).toMatchInlineSnapshot(` Array [ diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts index 5a3f902bf22fe..3ea0958b1f27d 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts @@ -7,7 +7,8 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { Logger } from 'kibana/server'; -import { getEsErrorMessage, IAbortableEsClient } from '../../../../alerting/server'; +import type { ElasticsearchClient } from 'src/core/server'; +import { getEsErrorMessage } from '../../../../alerting/server'; import { DEFAULT_GROUPS } from '../index'; import { getDateRangeInfo } from './date_range_info'; @@ -16,14 +17,14 @@ export type { TimeSeriesQuery, TimeSeriesResult } from './time_series_types'; export interface TimeSeriesQueryParameters { logger: Logger; - abortableEsClient: IAbortableEsClient; + esClient: ElasticsearchClient; query: TimeSeriesQuery; } export async function timeSeriesQuery( params: TimeSeriesQueryParameters ): Promise { - const { logger, abortableEsClient, query: queryParams } = params; + const { logger, esClient, query: queryParams } = params; const { index, timeWindowSize, timeWindowUnit, interval, timeField, dateStart, dateEnd } = queryParams; @@ -128,7 +129,7 @@ export async function timeSeriesQuery( // console.log('time_series_query.ts request\n', JSON.stringify(esQuery, null, 4)); try { - esResult = (await abortableEsClient.search(esQuery, { ignore: [404] })).body; + esResult = (await esClient.search(esQuery, { ignore: [404], meta: true })).body; } catch (err) { // console.log('time_series_query.ts error\n', JSON.stringify(err, null, 4)); logger.warn(`${logPrefix} error: ${getEsErrorMessage(err)}`); diff --git a/x-pack/plugins/triggers_actions_ui/server/data/routes/time_series_query.ts b/x-pack/plugins/triggers_actions_ui/server/data/routes/time_series_query.ts index 1f1cc8d121c75..05203d9076e22 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/routes/time_series_query.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/routes/time_series_query.ts @@ -14,7 +14,6 @@ import { } from 'kibana/server'; import { Logger } from '../../../../../../src/core/server'; import { TimeSeriesQueryParameters } from '../lib/time_series_query'; -import { createAbortableEsClientFactory } from '../../../../alerting/server'; import { TimeSeriesQuery, TimeSeriesQuerySchema, TimeSeriesResult } from '../lib/time_series_types'; export type { TimeSeriesQuery, TimeSeriesResult } from '../lib/time_series_types'; @@ -42,13 +41,9 @@ export function createTimeSeriesQueryRoute( ): Promise { logger.debug(`route ${path} request: ${JSON.stringify(req.body)}`); - const abortableEsClusterClient = createAbortableEsClientFactory({ - scopedClusterClient: ctx.core.elasticsearch.client, - abortController: new AbortController(), - }); const result = await timeSeriesQuery({ logger, - abortableEsClient: abortableEsClusterClient.asCurrentUser, + esClient: ctx.core.elasticsearch.client.asCurrentUser, query: req.body, }); diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index 4e026c20b579a..dd37f0cc8b67e 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -594,7 +594,7 @@ function getCancellableRuleType() { }, }; - await services.search.asCurrentUser.search(query as any); + await services.scopedClusterClient.asCurrentUser.search(query as any); if (doLongPostProcessing) { await new Promise((resolve) => setTimeout(resolve, 10000)); From f3a5b4102555d0db91604edbdfb74f8a7cfdd5f2 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Mar 2022 20:05:02 -0600 Subject: [PATCH 139/140] lazy-load view-underlying-data args --- .../public/app_plugin/show_underlying_data.ts | 5 +- .../lens/public/embeddable/embeddable.tsx | 94 +++++++------------ .../view_underlying_data_action.ts | 2 +- 3 files changed, 41 insertions(+), 60 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts index e1956542f8def..d15ec9460b30f 100644 --- a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts +++ b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts @@ -53,7 +53,10 @@ export function getLayerMetaInfo( currentDatasource: Datasource | undefined, datasourceState: unknown, activeData: TableInspectorAdapter | undefined, - capabilities: RecursiveReadonly + capabilities: RecursiveReadonly<{ + navLinks: Capabilities['navLinks']; + discover: Capabilities['discover']; + }> ): { meta: LayerMetaInfo | undefined; isVisible: boolean; error: string | undefined } { const isVisible = Boolean(capabilities.navLinks?.discover && capabilities.discover?.show); // If Multiple tables, return diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 50473fe8aa286..a8eda7d73bad6 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import { isEqual, uniqBy } from 'lodash'; +import { isEqual } from 'lodash'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { render, unmountComponentAtNode } from 'react-dom'; -import { Filter } from '@kbn/es-query'; +import { DataViewBase, Filter } from '@kbn/es-query'; import { ExecutionContextSearch, Query, @@ -21,13 +21,14 @@ import { import type { PaletteOutput } from 'src/plugins/charts/public'; import type { Start as InspectorStart } from 'src/plugins/inspector/public'; -import { Subject, Subscription } from 'rxjs'; +import { Subscription } from 'rxjs'; import { toExpression, Ast } from '@kbn/interpreter'; import { RenderMode } from 'src/plugins/expressions'; import { map, distinctUntilChanged, skip } from 'rxjs/operators'; import fastIsEqual from 'fast-deep-equal'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { METRIC_TYPE } from '@kbn/analytics'; +import { RecursiveReadonly } from '@kbn/utility-types'; import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; import { ExpressionRendererEvent, @@ -131,7 +132,7 @@ export interface LensEmbeddableDeps { canSaveVisualizations: boolean; canSaveDashboards: boolean; navLinks: Capabilities['navLinks']; - discover?: Capabilities['discover']; + discover: Capabilities['discover']; }; usageCollection?: UsageCollectionSetup; spaces?: SpacesPluginStart; @@ -161,7 +162,7 @@ function getViewUnderlyingDataArgs({ activeDatasource, activeDatasourceState, activeData, - indexPatterns, + dataViews, capabilities, query, filters, @@ -170,9 +171,9 @@ function getViewUnderlyingDataArgs({ activeDatasource: Datasource; activeDatasourceState: unknown; activeData: TableInspectorAdapter | undefined; - indexPatterns: IndexPattern[]; + dataViews: DataViewBase[] | undefined; capabilities: LensEmbeddableDeps['capabilities']; - query: Query; + query: Query | undefined; filters: Filter[]; timeRange: TimeRange; }) { @@ -191,7 +192,7 @@ function getViewUnderlyingDataArgs({ query, filters, meta, - indexPatterns + dataViews ); return { @@ -248,9 +249,6 @@ export class Embeddable private viewUnderlyingDataArgs?: ViewUnderlyingDataArgs; - private waitVUDArgsLoaded: Promise; - private markVUDArgsLoaded: (loaded: boolean) => void; - constructor( private deps: LensEmbeddableDeps, initialInput: LensEmbeddableInput, @@ -263,21 +261,6 @@ export class Embeddable }, parent ); - - const _vudSubscription = new Subject(); - - this.markVUDArgsLoaded = (loaded: boolean) => { - _vudSubscription.next(loaded); - _vudSubscription.complete(); - }; - - this.waitVUDArgsLoaded = new Promise((resolve) => { - const subscription = _vudSubscription.subscribe((loaded) => { - resolve(loaded); - subscription.unsubscribe(); - }); - }); - this.lensInspector = getLensInspectorService(deps.inspector); this.expressionRenderer = deps.expressionRenderer; this.initializeSavedVis(initialInput) @@ -477,7 +460,6 @@ export class Embeddable searchSessionId: containerState.searchSessionId, }; this.embeddableTitle = this.getTitle(); - this.updateViewUnderlyingDataArgs(); isDirty = true; } return isDirty; @@ -485,7 +467,6 @@ export class Embeddable private updateActiveData: ExpressionWrapperProps['onData$'] = (_, adapters) => { this.activeDataInfo.activeData = adapters?.tables?.tables; - this.updateViewUnderlyingDataArgs(); if (this.input.onLoad) { // once onData$ is get's called from expression renderer, loading becomes false this.input.onLoad(false); @@ -695,59 +676,56 @@ export class Embeddable } } - private async updateViewUnderlyingDataArgs() { - if (!this.activeDataInfo.activeData || !this.externalSearchContext) { - // This function is called both when activeData and externalSearchContext are loaded - // because we're not sure which one will load first. But, both must be loaded before - // we actually run this. - return; + private async loadViewUnderlyingDataArgs(): Promise { + if ( + !this.activeDataInfo.activeData || + !this.externalSearchContext || + !this.externalSearchContext.timeRange + ) { + return false; } const activeDatasourceId = getActiveDatasourceIdFromDoc(this.savedVis); - if (activeDatasourceId) { - this.activeDataInfo.activeDatasource = this.deps.datasourceMap[activeDatasourceId]; - const docDatasourceState = this.savedVis?.state.datasourceStates[activeDatasourceId]; - - this.activeDataInfo.activeDatasourceState = - await this.activeDataInfo.activeDatasource.initialize( - docDatasourceState, - this.savedVis?.references - ); + if (!activeDatasourceId) { + return false; } + this.activeDataInfo.activeDatasource = this.deps.datasourceMap[activeDatasourceId]; + const docDatasourceState = this.savedVis?.state.datasourceStates[activeDatasourceId]; + + this.activeDataInfo.activeDatasourceState = + await this.activeDataInfo.activeDatasource.initialize( + docDatasourceState, + this.savedVis?.references + ); + const viewUnderlyingDataArgs = getViewUnderlyingDataArgs({ - activeDatasource: this.activeDataInfo.activeDatasource!, + activeDatasource: this.activeDataInfo.activeDatasource, activeDatasourceState: this.activeDataInfo.activeDatasourceState, activeData: this.activeDataInfo.activeData, - indexPatterns: this.indexPatterns!, + dataViews: this.indexPatterns, capabilities: this.deps.capabilities, - query: this.externalSearchContext.query!, - filters: this.externalSearchContext.filters!, - timeRange: this.externalSearchContext.timeRange!, + query: this.externalSearchContext.query, + filters: this.externalSearchContext.filters || [], + timeRange: this.externalSearchContext.timeRange, }); const loaded = typeof viewUnderlyingDataArgs !== 'undefined'; - if (loaded) { this.viewUnderlyingDataArgs = viewUnderlyingDataArgs; } - - this.markVUDArgsLoaded(loaded); + return loaded; } /** * Returns the necessary arguments to view the underlying data in discover. */ - public async getViewUnderlyingDataArgs() { - const loaded = await this.waitVUDArgsLoaded; - if (loaded) { - return this.viewUnderlyingDataArgs; - } + public getViewUnderlyingDataArgs() { + return this.viewUnderlyingDataArgs; } public getCanViewUnderlyingData() { - // if loading the underlying data args fails - return this.waitVUDArgsLoaded; + return this.loadViewUnderlyingDataArgs(); } async initializeOutput() { diff --git a/x-pack/plugins/lens/public/trigger_actions/view_underlying_data_action.ts b/x-pack/plugins/lens/public/trigger_actions/view_underlying_data_action.ts index 54624392a3dbc..20adfb4cd549b 100644 --- a/x-pack/plugins/lens/public/trigger_actions/view_underlying_data_action.ts +++ b/x-pack/plugins/lens/public/trigger_actions/view_underlying_data_action.ts @@ -26,7 +26,7 @@ export const viewUnderlyingDataAction = (discover: DiscoverStart) => return isCompatible; }, execute: async (context: { embeddable: Embeddable }) => { - const args = await context.embeddable.getViewUnderlyingDataArgs(); + const args = context.embeddable.getViewUnderlyingDataArgs()!; discover.locator?.navigate({ ...args, }); From 868f0e8b83febd33fae295581f2ebd7c675864a6 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Mar 2022 20:09:42 -0600 Subject: [PATCH 140/140] only initialize datasource state the first time --- x-pack/plugins/lens/public/embeddable/embeddable.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index a8eda7d73bad6..4f865d1dc147b 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -693,11 +693,13 @@ export class Embeddable this.activeDataInfo.activeDatasource = this.deps.datasourceMap[activeDatasourceId]; const docDatasourceState = this.savedVis?.state.datasourceStates[activeDatasourceId]; - this.activeDataInfo.activeDatasourceState = - await this.activeDataInfo.activeDatasource.initialize( - docDatasourceState, - this.savedVis?.references - ); + if (!this.activeDataInfo.activeDatasourceState) { + this.activeDataInfo.activeDatasourceState = + await this.activeDataInfo.activeDatasource.initialize( + docDatasourceState, + this.savedVis?.references + ); + } const viewUnderlyingDataArgs = getViewUnderlyingDataArgs({ activeDatasource: this.activeDataInfo.activeDatasource,